JVM内存管理机制( 二 )


本地方法栈
本地方法栈与虚拟机栈所发挥的作用是非常相似的 , 它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务 , 而本地的方法栈则为虚拟机使用到的Native方法服务 。
在虚拟机规范中对本地方法栈中方法使用的语言 , 使用方式和数据结构并没有强制规定 , 因此具体的虚拟机可以自由实现它 。甚至有的虚拟机(譬如 Sun HotSport虚拟机)直接就把本地方法栈和虚拟机栈合二为一 , 本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常 。
Java堆
对于大多数应用来说 , Java堆是Java虚拟机所管理的内存中最大的一块 。Java堆是被所有线程共享的一块内存区域 , 在虚拟机启动时创建 。此内存区域的唯一目的就是存放对象实例 , 几乎所有的对象实例都在这里分配内存 。
Java堆是垃圾收集管理器的主要区域 。因此很多时候也被称做“GC”堆 。从内存回收的角度来看 , 由于现在收集器基本都采用分代收集算法 。所以Java堆中还可以细分为:新生代和老年代:再细致一点的有Eden空间、From Survivor空间、ToSurvivor空间等 。
从内存分配的角度来看 , 线程共享的Java堆中可能划分出多个线程私用的分配缓冲区 。不过无论如何如何划分 , 都与存放内容无关 , 无论哪个区域 , 存储的都任然是对象实例 , 进一步划分的目的是为了更好地回收内存 , 或者更快地分配内存 。
Java堆可以处理物理上不连续的内存空间 , 只要逻辑上是连续的即可 。如果在堆中没有内存完成实例分配 , 并且堆也无法再扩展时 , 将会抛出OutOfMenoryError异常 。
方法区
方法区与Java堆一样 , 是各个线程共享的内存区域 , 它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 。
方法区也叫永久代 , 在过去(自定义类加载器还不是很常见的时候) , 类大多是”static”的 , 很少被卸载或收集 , 因此被称为“永久的(Permanent)” 。
虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分 , 但是它却有一个别名叫做Non-Heap(非堆) , 目的应该是与Java 堆区分开来 。同时 , 由于类class是JVM实现的一部分 , 并不是由应用创建的 , 所以又被认为是“非堆(non-heap)”内存 。
运行时常量池
运行时常量池是方法区的一部分 , Class文件中除了有类的版本、字段、方法、接口等描述信息外们还有一项信息是常量池 , 用于存放编译期生成的各种常量和符合引用 , 这部分内容将在类加载后进入方法区的运行时常量池中存放 。
直接内存
直接内存并不是虚拟机运行时数据区的一部分 , 也不是Java虚拟机规范中定义的内存区域 。在JDK1.4中新加入了MIO(New Input/Output)类 , 引入了一种基于通道 。(Channel)与缓冲区(Buffer)的I/O方式 , 他可以使用Native函数库直接分配堆外内存 , 然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作 。
这样能在一些场景中显著提高性能 , 因此避免了在Java堆和Native堆中来回复制数据 。
new 一个对象 发生了什么? 
对象创建
Java是一门面向对象的编程语言 , 在Java程序运行过程中无时无刻都有对象被创建出来 , 在语言层面只是使用new关键字 , 而在虚拟机中 , 对象的创建又是怎样一个过程呢?

JVM内存管理机制

文章插图
 
 
1.类加载检查
虚拟机遇到一条new 指令时 , 首先将去检查这个指令的参数是否能在常量池中定位到一个类的符合引用 , 并且检查这个符合引用代表的类是否已被加载、解析和初始化过 。如果没有 , 那必须先执行相应的类加载过程 。
 
2.对象分配内存
在类加载检查通过后 , 接下来虚拟机将为新生对象分配内存 。对象所需内存的大小在类加载完成后便可完全确定 , 为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来 。根据Java堆中的内存是否规整 , 有2种处理方式:


推荐阅读