JVM笔记
2022-01-17 本文已影响0人
NazgulSun
jvm优化的一些好的文章:
https://www.jianshu.com/p/af2e21258987
1, jvm 虚拟机的old 晋升;
- edge 分配对象,分配不下;出发垃圾回收,还是分配不下,放入old【大对象情况】
- edge 中的对象在垃圾回收时存活,要放入to 区; 但是放不下,则放入 old 【高并发下,大量对象存活,占满 survior区】
- 每次minGC,对survior区的对象年龄+1;当超过15,则放入old。阈值可以通过参数调整;
- 如果survior区域内,同一年龄的对象超过空间一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
这其实是一个动态预防机制,比如MaxTenuringThreshold设置的过大,原本应该晋升的对象一直停留在Survivor区,直到Survivor区溢出,
一旦溢出发生,Eden+Svuvivor中对象将不再依据年龄全部提升到老年代,这样对象老化的机制就失效了。
2,逃逸分析
- 如果一个对象,仅仅在一个方法中访问,不会被其他线程引用;对象可以不被在heap中被分配;而是直接在stack 空间分配,而不用给gc带来压力;
- 分配内存和回收变快,性能变好;
- 同时逃逸分析还可以用来优化多线程代码,如果不可能被外面的线程访问,则可以把相关的同步器取消;
3,java8 的永久代方法区 - 移动到了metaspace,而且metaspace不是在堆中,而是使用宿主机的内存。据说也没什么特别的原因,就是为了和jrocket保持一致;
- class 相关的常量池和运行常量池保存在metaspace中。
- 字符常量池,静态变量,包装常量池等都放在了堆中可以做更好的回收,也导致了intern方法在java8的改变。比如一个字符串在heap中了,比如 String a = new("abc"),那么我们就可以使用
intern()的方法复用这个字符串,让他在运行期常量池中。 比如 String const= a.intern(); 会比 const="abc"更加节省内存; - 一种感觉就是class 相关的比较稳定不变放在meta,而运行时变化的,需要更好的回收的放在java heap中;
4,对象的内存布局
- 对象头,哈希值,垃圾回收代,锁标志,偏向线程的信息;
- 类型指针,指向meta区class的相关信息;
- 实例数据,对象各个字段的相关信息;
- string相关
- jdk9 开始,transfre char[] to byte[];代替了utf-16的固定方式,而使用 动态编码的方式来节省空间;
- 字符串常量池,是一个hashTable;jdk7默认60013;
- 当字符串太多的时候,常量池hashtable,冲突就会很多,会影响性能;
- jdk8,sringtable和静态变量表都放在了heap中【和其他对象一样分配】;便于回收。
- 常量与常量的拼接,结果在常量池,因为编译器优化;如果是常量+变量则一定是heap中,+号是StringBuilder的操作;
final 修饰的 s1+s2,采用的也是编译器优化,不用new string;stringbuffed.append方法效率高于a+b,因为每次都会调用 newStringBuilder; - printstringtableStatistics可以打印 stringtable的情况,他也会做垃圾回收;
- jvm中有很多string(25%),G1中优化了堆中string的对象,利用不可变现,可以对重复对象去重;
6.垃圾回收相关
- mark-sweep,标记可以打对象,标记记录在对象header中,sweep遍历所有对象,check-header并且回收设置空闲;最大的问题,STW+碎片化,大对象就很难办。
- copy算法;核心思想双存储区,eden+survie区;简单高效,无碎片;copy的时间是很长的,而且需要指针重定向;且浪费一部分空间;比较适合活的对象比较少的;
所以新生代比较适合复制算法;老年代不太适合; - mark-compact算法;将存活的对象放到一端;无碎片;也是半复制算法,copy和指针重定向问题依旧存在;
- 随着heap越来越大,一次收集会造成stw比较长,增量收集的思想开始流行,也就是一次收集一部分,多次收集类似于G1的思想;
7.垃圾回收相关概念
- 内存泄漏,对象不再使用,但是却用无法被gc回收;通常与外部链接的一些socket,jdbc不用的时候需要关闭,否则就被浪费了;
- stw无法避免,只是在不同的GC算法下有不同的大小;
- 并发(concuurent)一段时间内,多个程序在同一个CPU上执行。时间片算法;并行,同一时刻有多个程序,需要多个CPU;在垃圾回收中有不同的语义;
并行,多个线程执行回收,对应串行;并发,gc线程和用户线程; - 安全点,能够让程序停下来做gc的时刻;安全区域,一段带来中,对象的引用关系不会发生变化;
- soft和弱引用等都可以设置一个引用队列,在回收的时候放入队列,可以跟踪对象回收的情况;soft引用二级缓存,弱引用可以三级缓存,gc时回收;
虚引用,随时回收,只是用于queue来获取一个gc通知机制; - Cms 并发执行,初始标记,并发标记,重新标记,并发删除。两处停顿,核心在于重新标记的停顿时间,三色标记法?只能把垃圾救回来,活得变垃圾无法识别,成为浮动垃圾,只能下次才回收。并发删除阶段无法压缩,因为会修改地址。提供了一个配置参数,强制压缩,但是会加大停顿时间。cms 核心问题,碎片化和浮动垃圾。可能退化成串行回收。我们jdk8默认的是并行回收器,是一个吞吐量优先的回收器。
优化tips
- 【eden gc时间非线性】增加 eden 大小,可降低频率,同时单次 minGC时间变化不大。因为存活的对象不会线性增加,扫描的线性时间开销较小;