本文主要介绍: JVM 内存分哪几个区,每个区的作用是什么
备注:
橙色:堆 和 方法区 ,属于jvm公有部分,可以进行调优
灰色:java栈,本地方法栈和计数器 属于jvm的私有部分,不可进行调优
一个对象从创建到被回收的过程是怎样的?
Personp=new Person() 100个对象
第一步:
100个对象创建之后,会存储到新生代中的伊甸园区
第二步:
经过一轮GC后,绝大多数的对象会被回收,仅剩下5个对象存活
第三步:
再创建100个对象+5=105对象
又经过第二轮gc之后,仍有5个存活,其中有2个是上一轮留下的
第四步:
经过15轮gc 后仍然存活的对象 会存入到养老区。
第五步:
养老区中也会进行GC,一旦养老代gc速度赶不上对象的产生速度了,就会OOM,出现jvm挂机
类是如何加载进内存中?
核心是需要classLoader
会涉及双亲委派机制所以分类自上而下的去罗列
Bootstrap ClassLoader根加载器 rt.jar
Extension ClassLoader扩展类加载器
Application ClassLoader 应用类加载器
用户自定义类加载器如果
自定义String
总结过程:
三大步 :加载---->连接---->初始化
七小步 :加载---->验证---->准备---->解析---->初始化---->使用---->卸载
加载:将class文件加载到内存中
验证:验证字节码文件的正确性
准备:为类的静态变量分配内存空间并赋予初始值
解析:类加载器加载类所需的其他类
初始化:为类以及变量赋予真正的值
使用:类的调用
卸载:类的垃圾回收
java 虚拟机主要分为以下几个区
- 方法区 1. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生
GC,在这里进行的 GC 主要是对方法区里的常量池和对类型的卸载 - 方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译
器编译后的代码等数据。 - 该区域是被线程共享的。
- 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。
该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也
会存在这个常量池中。 - 虚拟机栈
- 虚拟机栈也就是我们平常所称的栈内存,它为 java 方法服务,每个方法在执行
的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口
等信息。 - 虚拟机栈是线程私有的,它的生命周期与线程相同。
- 局部变量表里存储的是基本数据类型、returnAddress 类型(指向一条字节码
指令的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,
也有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在
编译器间确定 - 操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量
表通过索引来访问,而是压栈和出栈的方式 - 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引
用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在
运行期转化为直接引用。 - 本地方法栈 本地方法栈和虚拟机栈类似,只不过本地方法栈为 Native 方法服务。
- 堆
java 堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实
例都在这里创建,因此该区域经常发生垃圾回收操作。 - 程序计数器:
内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的
字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数
器完成。该内存区域是唯一一个 java 虚拟机规范没有规定任何 OOM 情况的区
域。
https://openjdk.org/jeps/122 介绍 静态变量、字符串常量从永久代移动到堆中