内存管理机制-内存模型与对象的访问
博客专区 > Garrry 的博客 > 博客详情
内存管理机制-内存模型与对象的访问
Garrry 发表于6个月前
内存管理机制-内存模型与对象的访问
  • 发表于 6个月前
  • 阅读 2
  • 收藏 1
  • 点赞 1
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

 

RoadMap

 

虚拟机中的逻辑结构

虚拟机中的内存结构根据虚拟机规范划分出了五个常用区域,这些区域各自有各自的用途,有些随用户线程而存在,有些则随虚拟机进程而存在。 
五个区域分别为,虚拟机栈,本地方法栈,程序计数器,堆,方法区

 

程序计数器 (Program Counter Register, PCR)

程序计数器是随线程而生的,它是线程独立的,用来记录当前线程的字节码与指令,保证下一次线程唤醒之后,能回到正确的执行位置,说白了就是线程唤醒能继续执行。 
程序计数器是一块非常小的内存区域,也是唯一一个没有规定任何OutOfMemoryError的区域

虚拟机栈 (Virtual Machine Stack, VM Stack)

虚拟机栈也是线程私有的,它描述了Java方法执行的内存模型:每个方法被执行是都会创建一个帧栈(Stack Frame) 用于存储 局部变量表 操作数栈 动态链接 方法出口。每一个方法被调用直至执行完成的过程,就是对应着一个帧栈在虚拟机栈中从入栈到出栈的过程。 
局部变量表存放了编译期可知的基本类型和引用类型。 
在这个区域中,虚拟机规范规定了两种异常情况,StackOverflowError:当线程请求栈的深度大于虚拟机允许的深度,将会抛出此异常。 OutOfMemoryErro:当虚拟机栈在动态扩展长度时,无法申请到足够内存时将会抛出此异常

本地方法栈 (Native Method Stack, NM Stack)

本地方法栈与虚拟机栈发挥的作用非常类似,主要是为本地方法服务,虚拟机栈是为Java 方法服务的。 有些虚拟机实现 如 HotSpot 已经将NM Stack 和VM Stack 合二为一了

 

堆 (Heap)

heap 是java 虚拟机管理的内存中最大的一块,也是线程共享的。在虚拟机启动时 自动创建。 
除了JIT编译和逃逸分析技术下的对象,绝大多数的对象实例和数组都在堆上分配。 
同时 堆也是 垃圾收集器(Garbage Collection)主要工作的区域。 
如果堆中没有可分配的内存空间会抛出OutOfMemoryError

方法区

方法区与堆一样 是线程共享的,用于存储常量,静态变量,加载的类信息,即时编译的代码数据块。虽说 虚拟机规范把方法区化为堆的一部分,它却又一个别名non-heap,有意把它和堆区分开了,有些地方也把这地方称为,永久代,主要是因为把GC收集扩展至这个区域了或者说用永久代来实现方法区的功能。 
当方法去无法满足内存分配需求时,将会抛出OutOfMemoryError 
方法去主要存有: 
*类信息 
*类变量 
*类型常量池 
*方法信息 
String常量池便是其中之一。

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java 
虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致 
OutOfMemoryError 异常出现,所以我们放到这里一起讲解。 
在JDK 1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel) 
与缓冲区(Buffer)的I/O 方式,它可以使用Native 函数库直接分配堆外内存,然 
后通过一个存储在Java 堆里面的DirectByteBuffer 对象作为这块内存的引用进行 
操作。这样能在一些场景中显著提高性能,因为避免了在Java 堆和Native 堆中来 
回复制数据。

堆中的分代划分

heap 中内存划分基于分代思想 主要分为 新生代,老年代和存活区。 

新生代 Eden

大多数情况下,对象在新生代中分配。当Eden区没有足够的空间的时候会发起一次MinorGC

老年代 Old Generation

大对象直接进入老年代,所谓大对象就是指需要大量连续内存空间的Java 对象,如数组,字符串等。 
同时基于分代思想,虚拟机给每个对象定义了一个对象年龄。当对象在Eden区经过MinorGC后还生存下来后,会被移动到Suivior Space中并记为年龄为1,并且在Suivior Space 每经过一次GC后 对其中存活的对象年龄加1,当对象年龄超过一定的阈值(默认为15)时,将会被移动到老年代。

存活区 Suivivor Space

存活区有点像青年代,用于那些新生代存活下来而未到达老年代的对象。 
同时 存活区分为From,to 两块空间相同的区域,作为复制回收算法的区域

如果只是按照对象年龄阀值来判断是否需要移动到老年代,难以应付多变的内存情况,加入在存活区有着非常大量的年轻对象(2,3,4,5,6,7岁等)以至于 存活区内存不太够用的情况。对于这种情况,survivor有一个动态年龄判定。在存活区中的相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就直接进入老年代。

对象访问方式

Object obj = new Object() 
Object obj 声明了一个引用对象 
new Object() 在堆中开辟了空间并生成对象 
所谓对象的访问方式是指,引用类型obj如何定位到堆中的具体对象Object()的。

虚拟机中主流的方式有两种直接指针和句柄池

句柄池

如果使用句柄访问方式,堆中会划分出一小块内存作为句柄池,reference存储的是对象的句柄地址,而句柄中包含了对象实例数据和各自数据的具体地址信息。优点在于,reference存储的是稳定的句柄地址,在对象被移动时,只会改变句柄中的实例数据指针,reference本身不需要改变。 

直接指针

如果使用饿了直接指针访问,reference中直接存储了对象的地址。次方式的优点在于节约对象访问方式,对于频繁的访问对象,直接指针节约了一个层级的访问时间。 

共有 人打赏支持
粉丝 21
博文 46
码字总数 48703
×
Garrry
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: