《java虚拟机》笔记- 第二章:Java内存区域与内存溢出异常(理论部分)
序:人活着嘛,不仅需要吸收,还需要产出,好久没有写博客了,也是手痒痒,刚好晨会有个同事分享了一下JVM,所以也想总结一下自己在看JVM注意的点,姑且写个第二章的笔记吧,哈哈,很显然这个系列会太监,毕竟时间有限,工作之后迫不得已。JVM也看了有一阵子,都是偏理论的,还不知道在项目中到底怎么处理栈溢出,内存溢出的问题,话不多说,带着问题看笔记。
一:为什么需要学习JVM?
java与c++的区别:内存动态分配 和 垃圾收集技术。
c/c++缺点:
1.需要给每一个新建的对象分配内存
2.对象不再使用需要释放内存
Java的优点:
1.不需要为每一个new 写配对的free/delete代码
2.不容易出现内存泄漏和内存溢出问题
Java把内存管理的权限给了JVM,当出现内存溢出的问题的时候,如果不了解JVM,是很难排查出错误之处。
二:JVM运行时数据区域?
三:JVM五个运行时数据区域的作用?
Q:程序计数器的作用?线程私有还是线程共享?
1.程序计数器
作用:当前线程所执行的字节码的行号指示器,字节码解释器通过改变计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、线程恢复都需要依赖程序计数器。
内存空间:较小
线程私有:生命周期与线程相同,每个线程都有自己的程序计数器,各线程之间的计数器互不影响。
特点:唯一不会出现OutOfMemoryError的区域。
若线程正在执行java方法,程序计数器记录的时正在执行的JVM字节码指令的地址;若正在执行的是Native方法,则计数器为空。
Q:JVM的作用?线程私有还是线程共享?会抛出异常的场景?
2.java虚拟机栈(服务于非Native方法)
作用:描述的是Java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧,存储局部变量表(编译器可知的各种基本数据、对象引用)、操作数栈、动态链接、方法出口。
线程私有:生命周期与线程相同
StackOverflowError:线程请求的栈深度 大于 JVM所允许的栈深度
OutOfMemoryError:jvm动态扩展时无法获取到足够的内存。
Q:本地方法栈于java虚拟机栈的区别?
3.本地方法栈(服务于Native方法)
与java虚拟机栈基本不同点:本地方法栈服务于native方法。java虚拟机服务于java方法。
Q:java堆的作用?与java虚拟机的区别?
4.java堆
作用:存放对象实例,所有对象实例及数组都在堆上分配内存。java堆是垃圾收集器管理的主要区域。
java堆:新生代、老年代;Eden、From Survivor、To Survivor。目的:更好的回收内存,更快的分配内存。
内存区域:最大
所有线程共享:在jvm启动的时创建
与java虚拟机的区别:
1.堆内存被线程共享,java虚拟机栈线程私有
2.在jvm启动的时候创建,与jvm生命周期相同;生命周期与线程相同。
3.堆内存大于栈内存
4.栈帧存放局部变量表,堆存放对象
5.方法区
线程共享
存储信息:存储已被JVM加载的类信息,变量,静态变量,即时编译器编译后的代码等数据。
四:对象创建的步骤?
Q:内存分配的方式?
1.指针碰撞
特点:内存绝对规整,用过的内存放在一边,空闲的内存放在另一边,中间放着指针作为分界点指示器,分配内存则移动对应大小。
2.空闲列表
特点:内存不规整,已使用内存与空闲的内存相互交错,JVM维护一个列表,记录哪些内存块时可用的,分配内存时从列表中找一块足够大的空间分配给对象。
Q:本地线程缓冲区(Thread Local Allocation Buffer,TLAB)?
A:JVM中对象的创建时非常频繁的。若使用指针碰撞分配内存,在并发情况下,给A分配内存,指针还没有来的急修改,对象B又同时使用原来的指针分配内存。
解决方案:1.对分配内存空间进行同步处理2.每个线程都有一个分配内存的区域,这个分配的区域叫本地线程缓冲区。
五:对象的内存布局?
六:对象的访问定位?
1.句柄访问:引用中存放的句柄地址,句柄存放的对象实例数据
优点:若对象改变,句柄地址不变,变的只是句柄指向的对象
缺点:比直接访问慢
2.直接指针访问
优点:比句柄访问快
缺点:对象实例改变时需要修改栈帧存储的内容