《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——java的内存管理机制之内存模型

本文以下三个方面介绍jvm内存模型:
1.内存区域
2.访问机制
3.常见内存导致问题

1.内存区域:虚拟机在运行时所划分的内存区域,各自有不同的用途,整体如下图:
《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——java的内存管理机制之内存模型_第1张图片
黄色区域为线程共享的,而白色区域线程不共享。
1.1、程序计数器:每个线程独有的,存储的是当前线程执行的当前字节码行号。字节码解释器执行时,依赖的内存区域。但如果是Native方法(本地方法栈中的方法),则不进行存储,值为Undefined,该区域是唯一没有规定OutOfMemoryError错误的区域。
1.2、虚拟机栈:也就是我们平时所说的“栈”,这是java方法执行的区域,每个方法被执行时,会产生栈帧,记录(局部变量,操作栈,方法出口等)方法信息。方法执行的过程,也就是栈帧从入栈到出栈的过程。除了64位基础变量占用2个slot,其余的都为一个。
1.3、本地方法栈:是虚拟机与C语言对接的方法区域,由native关键字修饰,执行过程如虚拟机栈相同,故有些虚拟机也将此两个区域二合一。
1.4、堆:在虚拟机启动时创建,存放java对象,也是垃圾回收器的主要工作区,包括新生代,老年代和线程私有的分配缓冲区,本区域可以在物理上不连续,只要逻辑上连续即可。区域线程共享。
1.5、方法区:很多人习惯上称为永久代,存储了一杯加载的各种元信息数据,类的信息,常量,静态变量等。垃圾回收很少在这里工作,但是常量池回收及类的卸载均在此区域。运行时常量池存放编译器生成的各种字面变量和符号引用,在类加载后存入。
2.直接内存:堆外内存,是直接能使用的内存的一部分,受制于总内存大小和寻址空间限制。工作机制是有native方法直接分配,然后在java堆内以directByteBuffer对象存储器引用信息。
3.访问机制:
3.1、过程:当我们new一个对象时,句柄(如String s )存储到java栈的本地变量表中,作为reference类型出现,而实例化(如new String())则存储在java堆中,形成结构化内存。
3.2、访问方式:
3.2.1、句柄访问:在java堆中开辟一个区域(句柄池)来存储到对象实例的指针,java栈通过该指针信息访问对象。对象的指针和实例是分开存储的。
《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——java的内存管理机制之内存模型_第2张图片
3.2.2、直接指针访问:对象和指针数据存储在同一区域,访问时,直接通过指针获取所在位置的实例。
《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——java的内存管理机制之内存模型_第3张图片
3.2.3、对比:
3.2.3.1、句柄访问的好处在于reference中存储的是稳定的句柄指针地址,对象移动时,不需要修改。
3.2.3.2、直接指针访问的优势是速度快,节省一次定位时间开销。
4.常见内存问题:
4.1、堆内存溢出:java堆存储的对象达到最大限制后产生的内存溢出现象。OOM是典型的此类问题,可以通过分析堆存储快照,找出原因,调整代码或者参数。
4.2、虚拟机栈和本地方法栈溢出:在此类问题出现时,需要分析堆栈信息,查找具体原因。一般的解决方案为减加线程数和更换64位虚拟机解决,如果无法这样做,可以尝试减少最大堆和减少栈容量来换取线程数。
4.2.1、线程请求栈深度大于虚拟机允许的最大深度
4.2.2、虚拟机扩展栈时无法申请到足够的内存。
4.3、运行时常量池溢出:
4.4、方法区溢出:主要是大量生成动态类所造成的。需要特别注意垃圾回收情况。
4.5、本机直接内存溢出
另外,在java虚拟机中,堆外内存是很难被注意到的,因为java内存和堆外内存共享主内存,所以一味的增加java内存,会伤害堆外内存的存储功能,而因为堆外内存没办法在虚拟机内监控,因此使用时,要小心,以防堆外内存溢出,导致程序失败。

你可能感兴趣的:(《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——java的内存管理机制之内存模型)