JVM-运行数据区-堆

139 阅读3分钟

五、运行时数据区-堆

(一)堆概述
  • 一个JVM实例只存在一个堆空间,在JVM启动的时候被创建
  • Java堆被所有的线程共享,不过有一小片区域可以划分为线程私有的缓冲区(Thread Local Allocation Buffer ,TLAB)
  • 《Java虚拟机》规定,堆可以处于物理上不连续的内存空间,但是在逻辑上它应该被认为连续的
(二)堆的细分内存结构

JDK 7 称为新生代 + 老年代 + 永久代 ;JDK 8 及之后永久带没有了,称为新生代 + 老年代 + 元空间

(三)设置堆空间的大小与查看
  1. -Xms : 起始内存
  2. -Xmx : 表堆内最大的内存
  3. 具体查看可以使用命令 jps 相关命令
  4. 或者用jvm自带的工具jvisualvm查看

image.png

(四)新生代与老年代的设置
  • -XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3 (默认)
  • -XX:SurvivorRatio=8 ,表示Eden与survicor区的比例
(五)堆对象分配的过程

image.png

  1. Eden区对象发生Minor GC ,进入Survicor区, 对应对象的计数器加1,当重复多次GC,对象还属于可达对象并且对象的计数器达到阈值15,则对象进入老年代。(阈值15可以通过配置调整)

  2. 如果对象Eden区放不下,S区放不下会直接进入Old区

(六)关于不同GC理解
  • 部分回收
    • 新生代 minor GC 从Eden 到 s0 s1
    • 老年代 major GC
    • 混合收集 Mixed GC 收集整个新生代及部分老年代,这款垃圾回收器目前只有G1在使用
  • 整堆回收(full GC):回收整个堆和方法区 major GC 比 minor GC速度要慢10倍,STW更长也就是用户线程卡顿时间更长,要尽量减少major GC
(七)内存分配策略
  1. 优先分配Eden区
  2. 大对象直接放入老年代
  3. 长期存活的对象放到老年代
  4. 动态对象年龄判断(如果S区中相同年龄的所有对象的总和大于S空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代)
(八)TLAB (Thread Local Allocation Buffer)
  1. 为什么要用TLAB
  • 堆区线程共享,访问修改对象势必造成性能的下降,因为需要锁机制确保对象安全
  1. TLAB是什么
  • 它是线程私有,默认开启,占Eden的1%,内存非常小;
  • 能够提升内存分配的吞肚量,是一种快速的分配策略

image.png

(九)JVM优化技术
  1. 栈上分配
  • 如果经过逃逸分析,一个对象没有逃逸出方法,会优先进行栈上分配
  1. 同步省略
  • 如果一个对象被发现只能从一个线程访问到,对这个对象的操作就可以不考虑同步,也就是锁消除
  1. 标量替换
  • 分析new 的对象的属性,如果像存在定义了像 int a = 10 、int b = 20 这样的数,那jvm会考虑将这类元素放置到局部变量表,避免去做堆区回收工作