计划真的赶不上变化,时间过得真快。废话不多说了,今天主要记录之前有同事遇到的一些坑分享出来。

一、封装类的应用会引起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的更多相关文章

  1. 常见踩坑案例(二)-Request method 'POST' not supported

    一 前言 最近涉及到与前后端的数据对接,按道理来说没一点压力结果被一前端童鞋带坑里去了(不过也是很久没写过这种前后端分离进行联调的事情了,如果是一个人全套弄的话就不会出现下面问题). 二 Reques ...

  2. 【常见踩坑】USB调试安装失败(Installation failed with message INSTALL_CANCELED_BY_USER)

    一.写在前面 最近一直在忙活着项目重构,忙活了一个多月(那是天天加班,不分昼夜呀,ps:这不是我司要求的哈),终于把沉积了三四年的老项目给重构了,目前在测试阶段,也总算有了点闲时来跟大家分享分享一些问 ...

  3. 【常见踩坑】】USB调试安装失败(Installation failed with message INSTALL_CANCELED_BY_USER)

    [参考]http://www.cnblogs.com/liushilin/p/6553918.html 问题:在USB安装调试(小米手机),出现如下错误 解决:1.小米手机解决办法见参考.登录小米账号 ...

  4. Python 踩坑之旅进程篇其三pgid是个什么鬼 (子进程\子孙进程无法kill 退出的解法)

    目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4.1 技术关键字 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: 2.7.14 Github: https: ...

  5. Python踩坑之旅其二裸用os.system的原罪

    目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4.1 技术关键字 1.5 填坑总结 2. 前坑回顾 2.1 Linux中, 子进程拷贝父进程哪些信息 2.2 Agent常驻进程选择& ...

  6. Python踩坑之旅其一杀不死的Shell子进程

    目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4 坑后扩展 1.4.1 扩展知识 1.4.1 技术关键字 1.5 填坑总结 1.1 踩坑案例 踩坑的程序是个常驻的Agent类管理进程 ...

  7. [代码修订版] Python 踩坑之旅 [进程篇其四] 踩透 uid euid suid gid egid sgid的坑坑洼洼

    目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4 技术关键字 1.5 坑后思考 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: 2.7.14 代码示例: 公 ...

  8. Python 踩坑之旅进程篇其四一次性踩透 uid euid suid gid egid sgid的坑坑洼洼

    目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4 技术关键字 1.5 坑后思考 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: 2.7.14 代码示例: 菜 ...

  9. [代码修订版] Python 踩坑之旅进程篇其五打不开的文件

    目录 1.1 踩坑案例 1.2 填坑和分析 1.2.1 从程序优化入手 1.2.2 从资源软硬限入手 1.4.1 技术关键字 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: ...

随机推荐

  1. node+express搭建个人网站(1)

    我的个人网站 http://yangchaojie.top/ 首先了解一下node Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个 ...

  2. 【bzoj5028】小Z的加油店 扩展裴蜀定理+差分+线段树

    题目描述 给出 $n$ 个瓶子和无限的水,每个瓶子有一定的容量.每次你可以将一个瓶子装满水,或将A瓶子内的水倒入B瓶子中直到A倒空或B倒满.$m$ 次操作,每次给 $[l,r]$ 内的瓶子容量增加 $ ...

  3. BZOJ4951 Wf2017Money for Nothing(决策单调性)

    按时间排序,显然可能存在于答案中的公司价格应该单调递减.然后就可以大胆猜想感性证明其有决策单调性.具体地,设f(i,j)表示第i个消费公司和第j个生产公司搭配的获利,f(i,j)=(ti-tj)*(c ...

  4. DjangoORM使用mysql注意

    注意事项1:需要在project下的setting里面做设置.让Django生成MySQL类型的数据库. 注意事项2:在Django内部,连MySQL的时候,需要添加下面2句代码: 4.******* ...

  5. TCP的拥塞控制 (一)

    拥塞控制不同于流量控制,拥塞控制是在拥塞发生时,发送方根据一定的反馈,主动调节自己的发送速率,以防止拥塞恶化的行为. 1.   网络拥塞 路由器是网络中的关键组件,其内部有一定量的缓冲区,用于缓存来不 ...

  6. 【MVVM Dev】多个具有依赖性质的ComboBox对数据的过滤

    一.前言 在界面编程中,我们常常会遇到具有依赖性质的ComboBox框,比如最常见的: 省/直辖市 => 地级市/区 => 区/街道 今天就说一下在WPF的MVVM模式中如何实现该功能 二 ...

  7. 洛谷 P4585 [FJOI2015]火星商店问题 解题报告

    P4585 [FJOI2015]火星商店问题 题目描述 火星上的一条商业街里按照商店的编号\(1,2,\dots,n\) ,依次排列着\(n\)个商店.商店里出售的琳琅满目的商品中,每种商品都用一个非 ...

  8. Codeforces 894.E Ralph and Mushrooms

    E. Ralph and Mushrooms time limit per test 2.5 seconds memory limit per test 512 megabytes input sta ...

  9. mac和linux下sed使用的不同

    http://note.youdao.com/noteshare?id=84a6a0eb3e3f1698c9de4f48db24226e

  10. STL源码分析-rbtree

    http://note.youdao.com/noteshare?id=4f8d16e565478b4e98bf2e56ce98a28e