Java虚拟机
一. Java内存介绍
1. Jvm虚拟机内存区域划分及作用详解
程序计数器(PC寄存器):是一块较小的内存空间,他可以看做是当前线程所执行的字节码的行号指示器.他处于线程独占区.如果线程执行的java方法,该计数器记录的是正在执行的虚拟机字节码指令的地址.如果正在执行的native方法,这个计数器的值为undefined,该区域是唯一一个在java虚拟机规范中没有规定仍和OutOfMemoryError情况的区域.
本地方法栈:为虚拟机执行native方法服务;
虚拟机栈:为虚拟机执行java方法服务;描述的是java方法执行的动态内存模型.每个方法在执行的同时都会创建一个栈帧用来存放存储局部变量表、操作数表、动态连接、方法出口等信息,每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表:
存放编译器可知的各种基本数据类型,引用类型,returnAddress类型.
局部变量表的内存空间在编译期完成分配,当进入一个方法时,这个方法需要在帧分配多少内存是固定的,在方法运行期间是不会改变局部变量表的大小.
大小:可能出现的异常(StackOverFlowError 栈内存溢出,OutOfMemoryError 内存溢出)
方法区:存储虚拟机加载的类信息,常量,静态变量,即时编译后的代码等数据(类的版本,字段方法,接口)
堆:存放对象实例;垃圾收集器管理的主要区域;堆这块区域是JVM中最大的,应用的对象和数据都是存在这个区域,这块区域也是线程共享的,也是 gc 主要的回收区,一个 JVM 实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行 -Xmx, -Xms大小控制内存
运行时常量池:属于方法区的一部分.存放编译后期生成的是各种字面量(字面量的意思就是值,比如int i=3,这个3就是字面量的意思)和符号引用.将在类加载后在进入方法取得运行时常量池中存放.
对象的创建:
如何给对象分配内存? 是由java堆是否规整决定的. java堆是由垃圾回收的策略所决定的
1. 指针碰撞:假设已使用的内存和未使用的内存分配是规整的.创建对象是会把指针想没有分配的内存方向移动.
2. 空闲列表:有一个列表,其中记录中哪些内存块有用,在分配的时候从列表中找到一块足够大的空间划分给对象实例,然后更新列表中的记录。这就叫做空闲列表
线程的安全性问题:高并发的,同一时刻会有多个对象在创建.
1. 线程的同步,加锁.执行的效率比较低
2. 本地线程分配缓冲(TLAB):每个线程在分配一块单独的内存.提高了性能.
对象的结构:
1. Header(对象头):
自身运行时的数据(Mark Word)
哈希值,GC分带年龄(为垃圾回收分代标记算法回收垃圾),锁状态标志,线程持有的锁,偏向线程ID 偏向时间戳
类型指针:对象指向类的元数据的指针
2. InstanceDate:数据的事例,存储对象的有效信息.
3. Padding:对齐填充内存的作用.相当于占位符.
对象的访问定位:
1. 使用句柄:java堆中会划分出一块内存来作为句柄池,引用变量中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息
解释图:在栈中有一个引用变量指向句柄池中一个句柄的地址,这个句柄又包含了两个地址,一个对象实例数据,一个是对象类型数据(这个在方法区中,因为类字节码文件就放在方法区中),
2. 直接指针:引用变量中存储的就直接是对象地址了
在堆中就不会分句柄池了,直接指向了对象的地址,对象中包含了对象类型数据的地址。
区别:这两种各有各的优势,
使用句柄来访问的最大好处就是引用变量中存储的是稳定的句柄地址,对象被移动(在垃圾收集时移动对象是很普通的行为)时就会改变句柄中实力数据指针,但是引用变量所指向的地址不用改变。
而使用直接指针访问方式最大的好处就是速度更快,节省了一次指针定位的时间开销,但是在对象被移动时,又需要改变引用变量的地址。在我们上面分析的例子中,就是使用的直接指针访问的方式。
二. 垃圾回收
1. 如何判定对象为垃圾对象
(1) 引用计数法
① 在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值+1,当引用失效的时候,计数器的值就-1.
② -verbose:gc -xx:+PrintGCDetail 打印垃圾回收的详细信息
(2) 可达性分析法:定义一个GCroot,从gcroot节点向下找是否有节点,有引用存活,没有节点,就是垃圾对象.
作为GCRoots的对象
虚拟机栈
方法区的类属性所引用的对象
方法区中常量所引用的对象
本地方法栈中引用的对象
2. 如何回收
(1) 回收策略
① 标记-清除算法
1) 效率问题
2) 空间问题
② 复制算法
1) 堆
a. 新生代
a) Eden 伊甸园
b) Survivor 存活区
c) Tenured Gen
b. 老年代
2) 方法区
3) 栈
③ 标记-整理算法
④ 分代收集算法
(2) 垃圾回收器
① Serial
1) 最基本,发展最悠久
2) 单线程收集器
② Parnew
③ Parallel
1) 复制算法(新生代收集器)
2) 多线程收集器
3) 达到可控制的吞吐量(CPU用于运行用户代码的时间与CPU消耗的总时间的比值)
吞吐量 = (执行用户代码的时间) / (指向用户代码的时间 + 垃圾回收所占 用的时间)
-XX:MaxGCPauseMillis 垃圾收集器最大停顿时间
1ms
-xx:GCTimeRatio 吞吐量大小
④ Cms:Concurrent mark Sweep 标记清除算法
1) 工作过程
a. 初始标记
b. 并发标记
c. 重新标记
d. 并发清理
2) 优点
a. 并发收集
b. 低停顿
3) 缺点
a. 占用大量的CPU资源
b. 无法处理浮动垃圾
c. 出现Concurrent Mode Failure
d. 空间碎片
⑤ G1收集器(标记整理算法)
1) 历史
2) 优势
a. 并行与并发
b. 分代收集
c. 空间整合
d. 可预测的停顿
3) 步骤
a. 初始标记
b. 并发标记
c. 最终标记
d. 筛选回收