JVM概念及内存调优

JVM内存模型

加载机制: 加载 -> 连接(验证 -> 准备 -> 解析) -> 初始化 -> 使用 -> 卸载

Java文件通过 javac 编译为字节码文件,在通过 java 命令运行(Java虚拟机开始工作)。
首先通过 类装载子系统 把字节码文件加载到 运行时数据区,并分配内存,最终用 字节码执行引擎 来运行运行时数据区的代码。

在这里插入图片描述

上面图中名称解释:

  • 类加载器(ClassLoader)在JVM启动时或者在类运行时将需要的class加载到JVM中。
  • 字节码执行引擎负责执行class文件中包含的字节码指令。
  • 内存区(也叫运行时数据区)是在JVM运行的时候操作所分配的内存区。
    • 方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
    • java堆(Heap): 存放new创建的对象和数组。这块是GC的主要区域。
    • java栈(Stack): 存放局部变量(基本类型变量和对象的引用变量)。
      栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java栈。
      在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。
      每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程(先进后出)。
    • 程序计数器(PC Register): 用于保存当前线程执行的内存地址。
      由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方。
    • 本地方法栈(Native Method Stack)和java栈的作用差不多,只不过是为JVM使用到的Native方法服务( java 调用非 java 代码的接口,该方法并非 Java 实现的,可能由 C 或 Python等其他语言实现的)。
      方法区和堆是被所有线程共享的。
      java栈、程序计数器、本地方法栈都是线程私有的,线程生就生,线程灭就灭,栈中的栈帧随着方法的结束也会撤销,内存自然就跟着回收了。

垃圾检测、回收算法

垃圾收集器一般必须完成两件事:检测出垃圾;回收垃圾。

垃圾检测:

  • 引用计数法: 给一个对象添加引用计数器,每当有地方引用它,计数器就加1;引用失效就减1。如果两个对象互相引用,计数不为0,导致无法回收。

  • 可达性分析算法: 以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。
    这里的根集一般包括java栈中引用的对象、方法区中常量及静态属性引用的对象、本地方法栈中引用的对象等。

垃圾检测:

这里就不说明 标记-清除、复制、标记-整理 这些回收算法了,网上比较多。

在这里插入图片描述

分代收集算法: 内存默认比例为:新生代:老生代 = 1:2
不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。

  • 新生代: 发生的 GC 称为 Young GC(也叫 Minor GC使用复制算法)

    • 三个区: Eden区,from Survivor区(简称S0),to Survivor 区(简称 S1),默认内存比例 Eden: S0: S1 = 8:1:1。
    • 原理:
      • 每次新创建的对象放进Eden区,当 Eden 区将满时,触发 Minor GC,大部分对象在短时间内都会被回收,所以经过 Minor GC 后只有少部分对象会存活。
      • 将存活对象移到 S0 区,同时对象年龄加一(对象的年龄即发生 Minor GC 的次数),最后把 Eden 区对象全部清理以释放出空间。
      • 当触发下一次 Minor GC 时,会把 Eden 区的存活对象和 S0 (或S1)中的存活对象(S0 或 S1 中的存活对象经过每次 Minor GC 都可能被回收)一起移到 S1 (Eden 和 S0 的存活对象年龄+1), 同时清空 Eden 和 S0 的空间。
      • 若再触发下一次 Minor GC,则重复上一步,只不过此时变成了从 Eden、S1 区将存活对象复制到 S0 区,每次垃圾回收,S0、S1 角色互换,都是从 Eden、S0(或S1) 将存活对象移动到 S1(或S0)。也就是说在 Eden 区的垃圾回收我们采用的是复制算法,因为在 Eden 区分配的对象大部分在 Minor GC 后都消亡了,只剩下极少部分存活对象(这也是为啥Eden:S0:S1 默认为 8:1:1 的原因),
        S0、S1区域也比较小,所以最大限度地降低了复制算法造成的对象频繁拷贝带来的开销。
  • 老生代: 发生的 GC 称为 Old GC(也称为 Full GC一般采用标记整理法)

    • 原理: 当对象的年龄达到了我们设定的阈值,则会从S0(或S1)晋升到老年代。
    • 大对象:
      • 当某个对象分配需要大量的连续内存时,此时对象的创建不会分配在 Eden 区,会直接分配在老生代,因为如果把大对象分配在 Eden 区,Minor GC 后再移动到 S0、S1 会有很大的开销(对象比较大,复制会比较慢,也占空间),也很快会占满 S0、S1 区,所以干脆就直接移到老年代。
        还有一种情况也会让对象晋升到老年代,即在 S0(或S1) 区相同年龄的对象大小之和大于 S0(或S1)空间一半以上时,则年龄大于等于该年龄的对象也会晋升到老年代。
  • 永久代(Java8以前)

Stop The World(简称 STW):停止用户线程的执行,即在 GC(minor GC 或 Full GC)期间,只有垃圾回收器线程在工作,其他工作线程则被挂起。
如果老年代满了,会触发 Full GC,Full GC 会同时回收新生代和老年代(即对整个堆进行GC),它会导致STW,造成挺大的性能开销。

调优工具:

  • jvisualvm:jdk自带的调优工具
  • Arthas:阿里巴巴开源调优工具
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值