八股文_常规_jvm

290 阅读4分钟

堆内存

运行时数据区.jpg

类加载器

将.class文件加载到内存中并转换为Class对象,jdk提供了下面的三个类加载器:

  • BootStrapClassLoader:启动类加载器,加载jre/lib下面的类
  • ExtClassLoader:扩展类加载器,加载jre/lib/ext下面的类
  • AppClassLoader:应用类加载器,加载classpath下面的类

双亲委派

  • 类加载器加载一个类时,首先请求它的父类加载器,如果父类加载过则返回,如果没有则继续向上请求,一直到根类加载器BootStrapClassLoader,如果启动类加载器也没有找到,则从上到下依次由子加载器加载
  • 设计优点:避免核心类库被篡改
  • jvm中判断一个类是不是已经被加载过的逻辑是:类名+对应的类加载器实例

双亲委派.jpg

堆空间

  • 堆空间是线程共享的
  • 堆的默认最大空间是物理内存的1/4
  • 堆分为新生代和老年代,新生代:老年代=1:2
  • 新生代又分为Eden区、s0区、s1区,Eden:s0:s1=8:1:1

对象进入老年代的方式:

  • 年龄超过某个阈值,这个是动态调整的,区间在7-15
  • 大对象可直接进入老年代,jdk8没有启用该配置
  • 动态年龄判断:当Survivor区中年龄从1到n的对象大小之和累加超过Survivor区的50%时,年龄大于等于n的对象直接进入老年代

元空间

  • jdk1.8叫元空间(之前叫永久代、方法区等),元空间存储类信息、字符串常量池等
  • 元空间使用本地内存,大小受物理内存限制,不受堆内存控制

程序计数器

线程私有,每个线程都有自己的程序计数器,记录待执行的下一条指令的地址,jvm中唯一不会出现内存溢出的地方

本地方法栈

  • 线程私有,本地方法:native修饰的方法,由c++等语言实现,用来调用操作系统的方法
  • 虚拟机栈存的是java方法调用过程中的栈帧,本地方法栈存的是本地方法调用过程中的栈帧
  • 也会出现内存溢出和栈溢出

虚拟机栈

  • 虚拟机栈是线程私有,每个线程对应一个栈,栈中放栈帧,通过-Xss设置栈大小

  • 局部变量表保存这个方法执行过程中的局部变量,操作数栈用来辅助计算

    操作数栈.jpg

场景排查

  • jps -l:查看java进程
  • jmap -heap pid:查看堆内存分布
  • jmap -histo:live 进程id | more:查看对象数量
  • -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/dump.hprof:堆内存溢出时自动生成快照

参数设置

  • -Xms:设置堆的初始大小
  • -Xmx:设置堆的最大内存
  • -XX:MetaspaceSize:设置元空间大小
  • -XX:MaxMetaspaceSize:设置元空间最大内存

垃圾回收算法

  • 复制算法:把内存分成两部分,每次只用其中一部分,需要进行回收时,将已经使用的内存中的对象移动到另一块内存中,然后将之前已经用过的那块内存回收,就是s0,s1区,高效且避免内存碎片
  • 标记-清除算法:先标记垃圾对象,然后直接清除,这种方式会产生很多内存碎片
  • 标记-整理算法:先标记垃圾对象,然后将存活的对象移动到一起,最后将其余对象回收

垃圾回收器

先用“可达性分析算法”找到没有被GC Roots引用的对象,然后使用“垃圾回收算法”回收对象,如果对象在 finalize 方法中不能拯救自己就会被回收(除非有性能衡量指标,否则尽量不要修改GC的配置参数,即改也必须基于大量的测试结果)

名称适用代际线程模式备注
Parallel新/老年代并发jdk8默认
ParNew新生代并发默认与CMS是一对
CMS老年代并发通过并发标记和回收减少STW
G1全堆并发jdk9+默认,将内存分成小块,分新老年代,jdk9+默认
ZGC全堆并发jdk11推出,将内存分成小块,不分新老代