一、JVM中常见的垃圾回收器
1.单程的垃圾回收器
serial 和serial old
特点:单线程的垃圾回收,在垃圾收集的时候会停止所有线程。只是用之前200M以内的内存。
2. 多线程垃圾回收器
parallel Scavenge 和parallel old(吞吐量最大)
parNew
特点:多线程垃圾回收器,吞吐量优先,自适应的分配和设置堆的大小。
3.响应优先的垃圾回收器。(STW减少)
CMS(响应优先):Concurrent Mark Sweep
- 初始标记:GCROOTS直接相连的对象进行标记。(时间短,stw)
- 并发标记:基于初始标记进行并发标记。(时间长,并发标记,不暂停)
- 重新标记:并发标记的过程中可能会发生引用的改变。(时间短,STW)
- 并发清理 :标记清除法可以并发清理。(时间长,不暂停)
为了减少STW的时间,并发标记里会有两个阶段
原理:在并发清理里尽量多的去做重新标记的活。
- 预清理
- 并发可中断预清理
1)预清理(一次)
- 如果老年代的对象B已经被标记为不可达。此时有个新生代(eden区)对象持有了B对象的引用,则把B标记为可达。
- 如果在老年代中,某些区域本来没被标记,并发标记的时候又被标记了,需要把哪个区域的数据标记被dirty区,为重新标记提供方向
复制代码
2)并发可中断预处理(循环执行)
- 当from和to区的对象持有老年代的引用(可达)。导致老年代在进行并发标记导致标记改变。(需要edne区内存达到2M以上)
- 如果在老年代中,某些区域本来没被标记,并发标记的时候又被标记了,需要把哪个区域的数据标记被dirty区,为重新标记提供方向
-可中断的条件:1)可以设置循环次数。2)可以设置时间。3)EDEN区的最大比例(大于比例退出循环)。
复制代码
CMS中的问题。
- cpu敏感:因为要并发处理,最好CPU核心数大于等于4
- 浮动垃圾:并发清理阶段会有新的垃圾产生。需要下次垃圾回收清理。所以需要提前触发垃圾回收。
- 内存碎片:标记清除法导致。当有内存但无法分配出连续的内存时会退化成serial old
二、 jvm调优
1.JVM内存的分代划分。
根据活跃数据来划分:活跃数据跑一周看数据稳定在多少
2. 扩容新生代,提高GC的效率。
复制算法的流程:
1.扫描新生代判断对象是否存活。
2.复制对象到幸存者0区。(时间更长)
复制代码
提高GC的原因:新生代的空间变大会导致发生GC的时间间隔变长。会导致更多对象在发生GC的时间间隔中就已经变成了垃圾,不会走复制到幸存者0区的流程。(少了2.复制的流程)
3.跨代引用如何避免Minor GC时扫描全堆。
跨代引用问题:只进行年轻代的回收,但是老年代会持有新生代的引用导致需要遍历老年代。
解决方案:card table(卡表)
卡表:是个boolean类型的数组。在老年代里每一块空间都会有一个boolean值来表示。当存在跨代引用的时候,对应的boolean值就会改变,也就是脏数据标记。当在进行垃圾年轻代垃圾回收的时候就会把脏数据一起扫描。
三、String
1.常量池
- class常量池:静态常量池,在.class文件里的.包含:字面量和符号引用。
- 运行时常量池:跟运行时有关系,符号引用转换成直接引用。
- 字符串常量池:有争议的池。
2.String
在1.8的时候是char[]
在1.9以后是byt[]
1)string对象的特点:不可被继承、不可修改
2) String str = “abc”会在字符串常量池中创建”abc”
3) String st = new String(“abc”).会多出一个对象。
4)String st = “a”+”b”+”c”;
编译后的.class会直接变成 String st = “abc”;
5) String a = “a”; String b = a +”b”;有变量的
是通过创建StringBuilder进行append拼接,最后new String()返回的。
6)String a = new String(“aa”).inter(); String b = new String(“aa”).inter();
会因为jdk版本不同导致结果不一致。
a、b本来是两个对象,但是因为调用inter()方法,返回的都是字符串常量池里”aa”的地址。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END