目录
为对象分配内存TLAB(Thread Local Allocation Buffer)
堆空间内存分布
手动设置堆空间
idea-run-edit configuration 选中要设置的类 -Xms:堆最小内存 -Xmx:堆最大内存,一般手动设置都会把最大内存和最小内存设置为相同的,这样就不会有扩容的情况,减少系统的压力
不去设置堆空间的话,默认-Xms是系统内存的1/64,-Xmx是系统内存的1/4
-XX:+PrintGCDetails 加上这句话控制台就可以打印gc细节出来
可以看到我们设置了堆空间600m,上面新生代+老年代+元空间是575m,因为在新生代中,还分为1个Eden Space和2个Suvivor Space,Suvivor Space中有一个是为了复制另一个Suvivor Space的,所以实际上加起来内存会少了25m
年轻代与老年代
内存划分
Eden区就是常说的伊甸园区,Survivor区就是常说的幸存者区
新生代与老年代堆空间比例与设置
默认新生代与老年代比例是1:2
新生代中Eden与两个Survivor的堆空间比例与设置
官方文档是说默认Eden与两个Survivor的比例是8:1:1, 但实际上比例是6:1:1 如下图所示,是150比25比25 6:1:1
要想达到8:1:1就得手动设置-XX:SurvivorRatio=8达到8:1:1
对象分配过程
- 对象首先在Eden区中new出来,然后Eden区满了,触发YGC将垃圾(下图红色方块代表垃圾)进行回收,然后把还有用的这两个对象就存放到幸存者to区 (图一中两个都是空的,即都是to区,所以优先放到S0区),此时Eden就已经清空了,并且S1为to区,S0为from区,两个方块的age年龄+1。
- YGC并不只是会对Eden区回收,也会对幸存者区回收,但是YGC一般都是在Eden区满了才会触发
- 第二次Eden区满时,同样对垃圾进行回收,然后把还有用的这个对象放到to区,此时S0不是空的,即是from区,所以将对象放到S1区,即to区;然后把S0区的所有还有用的对象再转移到S1区并且age年龄+1,如果已经没用的对象,就会被回收掉。此时S0为to区。
- 依次循环,如果对象年龄超过15就会晋升到老年代中
对象分配过程总结
- 新对象申请时,Eden放不下,进行YGC,YGC时先判断Eden区中还有用的对象幸存者区能不能放得下,放得下则放幸存者区,如果放不下,就直接将Eden区中这个还有用的对象晋升到老年代;然后再判断幸存者区是否有需要回收的对象,然后把幸存者区的from区对象全部转移到to区,如果from区对象to区放不下,转存到老年代;然后判断此时新对象Eden区能不能放得下,如果还放不下,就判断老年代能不能放,如果老年代还放不下,进行FGC,如果FGC后老年代还放不下,OOM内存溢出错误。
- 注:YGC指(Young GC/Minor GC),FGC指Full GC
Minor GC、Major GC、Full GC
概述
年轻代GC(Minor GC)的触发条件
- Eden区满就会触发GC,触发GC也会去回收Survivor区中没用的对象
- STW指 Stop The World的缩写
老年代GC(Major GC)的触发条件
FullGc触发条件
内存分配策略(对象提升规则)
为对象分配内存TLAB(Thread Local Allocation Buffer)
为什么有TLAB?
什么是TLAB(快速分配策略)
- 每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定。
- TLAB是在Eden区中分配的一块非常小的区域
- jdk默认是开启TLAB的
堆空间参数设置总结
- 之所以是老年代最大可用连续空间大于新生代所有对象总空间才是安全的原因是:有可能新生代中所有对象都还存活,全部需要担保到老年代,所以只有在大于的情况下,才能保障不会OOM
堆是分配对象的唯一选择吗?
概述
逃逸分析
- JDK 6u23的版本之后默认就开启了逃逸分析
- -XX:+DoEscapeAnalysis开启逃逸分析
- -XX:+PrintEscapeAnalysis 报错,不知什么原因,
- 这种在方法中运行完就跟着结束的对象,称为没有发生逃逸,可以栈上分配,即在栈上分配内存
- 下面toString的新String对象虽然发生了逃逸,逃出了方法,但是String的占用内存是远比StringBuffer小的,所以下面这算是一个优化
- 具体逃逸分析
逃逸分析:代码优化
同步省略
栈上分配与标量替换
- 概念如下
- 不发生逃逸的对象就能在栈上分配,栈上分配前提必须是开启了标量替换,默认情况栈上分配和标量替换都是开启的,也可以通过-XX:+DoEscapeAnalysis(开启栈上分配) -XX:+EliminateAllocations(开启标量替换,允许将对象打散分配到栈上)手动开启或关闭;什么是标量如下
可分解的对象就称为聚合量
- 没有逃逸分析和标量替换,可以看到大量GC,说明分配到堆上,并且执行时间是445ms
- 开启逃逸分析和标量替换,可以看到并没有出现GC,说明是栈上分配,并且执行时间是4ms
逃逸分析小结:并不成熟