常见踩坑案例(一) subList引起FULLGC
计划真的赶不上变化,时间过得真快。废话不多说了,今天主要记录之前有同事遇到的一些坑分享出来。
一、封装类的应用会引起NPE异常
对于其他对象的应用,一般在使用之前会判断它是否为空,如果不为空才会使用它以及它里面的一些属性值。但是对于基本类型的封装类型,就有很多人漏掉对于它的判断。
就在前面几天有位同事问我说这段代码它怎么会报空指针呢? 先模拟下这个场景下的代码:
// 第三方的实体类对象
class Count{
Integer total;
String name;
String leavingNum;
//...
} public class DemoTest { public void fun() {
Count count = new Count();
// 经过一系类对应值的获取之后
// 需要判断total是否为0,如果为0就进行其他的业务操作。
if (count.total == 0) {
// 比方说打印日志。
}
} @Test
public void test() {
fun();
}
}
他给我看的代码就是类似fun()方法中的代码, 我一看这个代码就感觉不对劲,我说你这个Count类下的total属性是int吗?如果是int就可以这么用。他说为什么? “如果是Integer类型,那么它的初始值不是0而是null, 而你上面的这些逻辑又不能保证total 一定会获取到数值,那么它就还有可能是null,你这样使用的话就有可能包NPE的问题。所以针对对象的使用提前判空更有保障。” 我接着说。 在我说的过程中他反应还是很快的,立马查看了这个实体类中total字段的类型,于是就明白了。 如果没有养成提前使用判空的习惯(除非你能保证一定会有值),老手都容易会踩这样的坑。比方说针对Boolean类型的使用,有很多人会直接这样的哦(这样肯定会有问题的)。
Boolean flag = null;
// 经过一系类操作处理
// 进行判断
if (flag) {
//......
}
另外针对基本类型的封装类型使用还有些要注意的请看这篇文章:https://www.cnblogs.com/yuanfy008/p/8321217.html
二、subList带来的隐患
现在有很多都是基于分布式服务,那应该会存在这个域对应数据需要同步到其他域下,然后这种同步必然会产生差异,需要一种自检的job去检测差异。打个比方有些商家自己有官网售卖自己的产品,也还有可能会在天猫开旗舰店售卖。假如它分配在天猫的商品信息是通过它本地天猫数据库同步过去的,那么这种难免会产生差异,特别是库存,如果一边多一边少就可能会导致超卖的情况。 所以这种情况需要有个job对比两边的差异,下面先简单模拟下事发情况(注意下面的用法):
public class SubListTest { @Test
public void test1() {
// 初始集合(有序)
List<Integer> list = new ArrayList<>();
list.addAll(Arrays.asList(1, 2, 3, 4)); // 业务场景:需要将list集合与很多场景下的数据进行对比,然后取出不同的。
// 对比的场景就不还原了,假设每次都是前面两个不同。 这里只列举四次对比,为了方便查看效果不使用for循环 // 第一次截取不同的数据
list = list.subList(0, 2);
// 查看list中有多少数据
System.out.println(list); // 查询有新的数据,往list中添加
list.addAll(Arrays.asList(5, 6));
//...
// 第二次截取
list = list.subList(0, 2);
System.out.println(list); // 查询有新的数据,往list中添加
list.addAll(Arrays.asList(7, 8));
//...
// 第三次截取
list = list.subList(0, 2);
System.out.println(list); // 查询有新的数据,往list中添加
list.addAll(Arrays.asList(9, 10));
//...
// 第四次截取
list = list.subList(0, 2);
System.out.println(list); }
}
看到上面代码其实很简单,输出结果大家也都知道。下面先一步一步的分析,然后再介绍在大量数据的情况下这会产生结果。
第一步:查看下list的内存分配地址,后面会有需要。
第二步:跳到第一次截取之后,看list有什么变化?
它的内存地址变了,也就说每次subList都会产生一个新对象,那么得查看下这subList的源码,而源码中确实是会产生一个新对象。但是请仔细SubList的构造函数,其中会存放它的父级对象。那么这会产生什么影响呢?请接着往下看。
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
} private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size; SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
// 后面方法省略
}
第三步:在第三次截取之前直接在源码中SubList构造函数中打断点,然后跳转进来,看看对应对象的属性值:
咋一看,这里面怎么param_1怎么来的?通过第二步查看SubList的源码再加上第一步的需要你留意list原生的的对象内存地址,你就知道param_1对应是这个list的根对象,它一直保留子对象新增的对象。那么大家想下,这种做法当遇到海量数据对比差异时会产生什么影响呢?
如果刚才看懂了上面所说的,那么肯定会明白这个list的根对象累积到后面肯定会变成大对象,这样会导致平凡的fullGc而且你还回收不掉。因为它一直在使用,直至这个程序运行结束。
那么像上面这种场景怎么优化解决呢? 可以这样考虑, 每次对比时候都弄一个新的list去获取差异,然后再把这个有差异的list添加至总的差异结果集中。(其实我们平时也不会用一个list去反反复复的subList)。如果大家有更好的优化,请留言探讨。
常见踩坑案例(一) subList引起FULLGC的更多相关文章
- 常见踩坑案例(二)-Request method 'POST' not supported
一 前言 最近涉及到与前后端的数据对接,按道理来说没一点压力结果被一前端童鞋带坑里去了(不过也是很久没写过这种前后端分离进行联调的事情了,如果是一个人全套弄的话就不会出现下面问题). 二 Reques ...
- 【常见踩坑】USB调试安装失败(Installation failed with message INSTALL_CANCELED_BY_USER)
一.写在前面 最近一直在忙活着项目重构,忙活了一个多月(那是天天加班,不分昼夜呀,ps:这不是我司要求的哈),终于把沉积了三四年的老项目给重构了,目前在测试阶段,也总算有了点闲时来跟大家分享分享一些问 ...
- 【常见踩坑】】USB调试安装失败(Installation failed with message INSTALL_CANCELED_BY_USER)
[参考]http://www.cnblogs.com/liushilin/p/6553918.html 问题:在USB安装调试(小米手机),出现如下错误 解决:1.小米手机解决办法见参考.登录小米账号 ...
- Python 踩坑之旅进程篇其三pgid是个什么鬼 (子进程\子孙进程无法kill 退出的解法)
目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4.1 技术关键字 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: 2.7.14 Github: https: ...
- Python踩坑之旅其二裸用os.system的原罪
目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4.1 技术关键字 1.5 填坑总结 2. 前坑回顾 2.1 Linux中, 子进程拷贝父进程哪些信息 2.2 Agent常驻进程选择& ...
- Python踩坑之旅其一杀不死的Shell子进程
目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4 坑后扩展 1.4.1 扩展知识 1.4.1 技术关键字 1.5 填坑总结 1.1 踩坑案例 踩坑的程序是个常驻的Agent类管理进程 ...
- [代码修订版] Python 踩坑之旅 [进程篇其四] 踩透 uid euid suid gid egid sgid的坑坑洼洼
目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4 技术关键字 1.5 坑后思考 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: 2.7.14 代码示例: 公 ...
- Python 踩坑之旅进程篇其四一次性踩透 uid euid suid gid egid sgid的坑坑洼洼
目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4 技术关键字 1.5 坑后思考 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: 2.7.14 代码示例: 菜 ...
- [代码修订版] Python 踩坑之旅进程篇其五打不开的文件
目录 1.1 踩坑案例 1.2 填坑和分析 1.2.1 从程序优化入手 1.2.2 从资源软硬限入手 1.4.1 技术关键字 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: ...
随机推荐
- Linux下objdump查看C程序编译后的汇编代码
http://m.blog.csdn.net/article/details?id=47747047 Uboot中start.S源码的指令级的详尽解析 http://www.crifan.com/fi ...
- stm32f4xx系统总线架构
最近有人在STMCU社区网站咨询如下问题: 由于实验需要,要用到STM32F407的两个DMA并用定时器触发,在使用过程中发现DMA1无法把GPIO的IDR上的数据传输到内存,调试过程中出现DMA1的 ...
- P4005 小 Y 和地铁
题目描述 小 Y 是一个爱好旅行的 OIer.一天,她来到了一个新的城市.由于不熟悉那里的交通系统,她选择了坐地铁. 她发现每条地铁线路可以看成平面上的一条曲线,不同线路的交点处一定会设有 换乘站 . ...
- Unified Networking Lab 安装使用IOL镜像
Unified Networking Lab 安装使用IOL镜像 Unified Networking Lab 很久以前,在一个星系远的地方,很远的工程师们为eBay寻找二手路由器来满足家庭实验的需求 ...
- Debug快捷键
Debug快捷键 1. F5单步调试进入函数内部2. F6单步调试不进入函数内部3. F7由函数内部返回到调用处4. F8一直执行到下一个断点5. F11 重新运行debug
- [CF1111C]Creative Snap
题目大意:有一个长度为$2^n(n\leqslant30)$的格子,有$k(k\leqslant10^5)$个球,分布在这些格子中,有两种消灭格子的方法: 1. 若一段格子长度大于等于$2$,可以对半 ...
- 51nod 1952 栈(单调队列)
用deque实时维护栈的情况. 数加入栈顶部,删掉栈顶部的数,相当于加入一个数,删掉最早出现的数,每次求最大值,这个直接记录一下就好了. 数加入栈底部,删掉栈顶部的数,相当于加入一个数,删掉最晚出现的 ...
- [学习笔记]FHQ-Treap及其可持久化
感觉范浩强真的巨 博主只刷了模板所以就讲基础 fhq-treap 又形象的称为非旋转treap 顾名思义 保留了treap的随机数堆的特点,并以此作为复杂度正确的条件 并且所有的实现不用旋转! 思路自 ...
- [转载]DataView详解
表示用于排序.筛选.搜索.编辑和导航的 DataTable 的可绑定数据的自定义视图. DataView的功能类似于数据库的视图,他是数据源DataTable的封装对象,可以对数据源进行排序.搜索.过 ...
- vijos 小三学算术
描述 小三的三分球总是很准的,但对于数学问题就完全没有想法了,他希望你来帮他解决下面的这个问题:对于给定的n,从1!.2!.3!.…….n!中至少删去几个数,才可以使剩下的数的乘积为完全平方数? 格式 ...