(给ImportNew加星标,提高Java技能)
作者:阿水
一千万个身份证号的内存占用
前些时间和一个小朋友聊一些技术的内容,我让他回去验证一下一千万个身份证号在java中要耗费多少内存,他写了个程序,得出来的结论是599MB,我掐指一算,如果通过数学推算的话,差了100多M,于是就和他赌10块钱,谁输谁给十块钱。这篇贴子就是为了要赚那10块钱。1.代码不太好看,标量命名a,b啥的2.for循环里是用同一个变量名来引用String对象,我判断他这么写的目的是避免再拿一个数组来存放的话,数组的内存占用就会带来误差,但是这么写的话就会引出GC的影响。不过我不是低估了这个小朋友,他说他把新生代内存调到1G了,而且加了-XX:+PrintGCDetails参数,从日志来看,没有进行GC。数学推算
1.在java中,所有对象的内存占用都会是8个字节的倍数,如果少于8个字节的倍数,则向上对齐到8的倍数。2.身份证是18位,在内存中是以char数组存储,一个空的char数组本身要16个字节,一个char是2个字节,所以是16+18*2=52个字节。•char数组本身也是一个特殊的对象,对象头是8个字节,外加一个数组长度是int类型,4个字节,一共12个字节,不是8的倍数,向上对齐到16。3.如果数组最终内存占用字节数不是8的倍数,则向上对齐到8的倍数,52不是8的倍数,则向上取56,占用56个字节。4.一个空String是24个字节,加上char数组的56个字节=80个字节,是8的倍数。5.1000万个身份证号,读到内存中,是1000万个String,总内存数是80*1000万字节=762.94M。String对象的最小内存占用 (bytes) = 8 * (int) ((((char数量 * 2) + 45) / 8实验推算
基于数学得出来的是理论值,而且对于JVM里内存对象的占用,要做很深入的研究才能完全掌握,比如不同的机器、操作系统、jdk、jvm参数配置等等,都会影响到内存的占用。数学推算可做为一个合理的值范围,不会出现大的偏差,最好还是通过实验来验证。char数组的真实内存情况
1.通过jps获得进程ID2.通过jamp导出对应进程的内存信息3.通过MAT工具分析char数组的内存情况•char[0]=16字节•char[2]=24字节•char[8]=32字节•char[13] =48字节•char[19] =56字节•char[36] =88字节•char[348] =712字节•char[14305] =28632字节我们在写程序和排查问题时,很难说所有的问题,你都有相应的知识储备。在解决问题时,最重要的点是思路,结合我们已有的知识,从问题发现规律,理清头绪。以上面char数组的内存情况来分析,我们可以有以下思路3.char[2]比char[0]多8个字节,而不是4个字节,为什么?4.为什么所有的内存占用都是8的倍数,不管怎么样都是8的倍数,这是不是造成char[2]比char[0]多8个字节的原因,JVM有内存对齐机制结论:char数组的内存占用=16+数组长度*2,如果最终结果不是8的倍数,则对齐到8的倍数。直接代码验证
• 空String数组,size为1000万,内存消耗为55728720字节• String数组,内有1000万身份证号,内存消耗为841801640字节• 二者相减,得786,072,920字节,等于750MB,与数学推算的762MB误差在2%以内。后记
1.以上结论和推算都是基于jdk1.8.0_92,不同版本可能会有差别。2.分析问题就和做数独一样,已知5个数,要把所有的数字解出来,中间可能需要试错。•知识面更广,就相当于拿到了一个简单的数独,81个数,已经填充了78个数了,你只需要解三个数就行
•你分析问题的思路会更解析,你有更多解数独的套路,顺着这些套路就可以一步步解答出来
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
好文章,我在看❤️