五、运行时数据区-堆
(一)堆概述
- 一个JVM实例只存在一个堆空间,在JVM启动的时候被创建
- Java堆被所有的线程共享,不过有一小片区域可以划分为线程私有的缓冲区(Thread Local Allocation Buffer ,TLAB)
- 《Java虚拟机》规定,堆可以处于物理上不连续的内存空间,但是在逻辑上它应该被认为连续的
(二)堆的细分内存结构
JDK 7 称为新生代 + 老年代 + 永久代 ;JDK 8 及之后永久带没有了,称为新生代 + 老年代 + 元空间
(三)设置堆空间的大小与查看
- -Xms : 起始内存
- -Xmx : 表堆内最大的内存
- 具体查看可以使用命令 jps 相关命令
- 或者用jvm自带的工具jvisualvm查看
(四)新生代与老年代的设置
- -XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3 (默认)
- -XX:SurvivorRatio=8 ,表示Eden与survicor区的比例
(五)堆对象分配的过程
-
Eden区对象发生Minor GC ,进入Survicor区, 对应对象的计数器加1,当重复多次GC,对象还属于可达对象并且对象的计数器达到阈值15,则对象进入老年代。(阈值15可以通过配置调整)
-
如果对象Eden区放不下,S区放不下会直接进入Old区
(六)关于不同GC理解
- 部分回收
- 新生代 minor GC 从Eden 到 s0 s1
- 老年代 major GC
- 混合收集 Mixed GC 收集整个新生代及部分老年代,这款垃圾回收器目前只有G1在使用
- 整堆回收(full GC):回收整个堆和方法区 major GC 比 minor GC速度要慢10倍,STW更长也就是用户线程卡顿时间更长,要尽量减少major GC
(七)内存分配策略
- 优先分配Eden区
- 大对象直接放入老年代
- 长期存活的对象放到老年代
- 动态对象年龄判断(如果S区中相同年龄的所有对象的总和大于S空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代)
(八)TLAB (Thread Local Allocation Buffer)
- 为什么要用TLAB
- 堆区线程共享,访问修改对象势必造成性能的下降,因为需要锁机制确保对象安全
- TLAB是什么
- 它是线程私有,默认开启,占Eden的1%,内存非常小;
- 能够提升内存分配的吞肚量,是一种快速的分配策略
(九)JVM优化技术
- 栈上分配
- 如果经过逃逸分析,一个对象没有逃逸出方法,会优先进行栈上分配
- 同步省略
- 如果一个对象被发现只能从一个线程访问到,对这个对象的操作就可以不考虑同步,也就是锁消除
- 标量替换
- 分析new 的对象的属性,如果像存在定义了像 int a = 10 、int b = 20 这样的数,那jvm会考虑将这类元素放置到局部变量表,避免去做堆区回收工作