首先java虚拟机规范将内存划分为如下几块:
1.栈区:该区域主要是存放方法临时变量、函数参数值等。由编译器自动分配释放,在具体方法结束之后由编译器自动释放。
2.静态取:部分永久存在于内存中,主要存放一些字符串常量、全局变量等。
3.堆区:该区域是分配给程序员控制的区域(其实也是由jvm统一控制),存放一些new的对象和数组,垃圾回收器会不定期查看该区域,然后通过回收机制进行垃圾回收。
4.代码区:存放二进制代码的区域,而且是多个对象共享的。
另外在《java虚拟机jvm高级特性与最佳实践》中则采取如下方法划分:
1.虚拟机栈:和上一样
2.堆区:和上面一样
3.程序计数器:主要是对编译过后的字节码行号指示器。主要是用来指向下一条需要执行的指令。
4.方法区:各线程共享,主要用来存放已被虚拟机加载过的类信息、常量、静态变量、即时编译后的代码。
在方法区下还单独提出一个 常量池
常量池中主要存放的是运行期产生的动态常量。这点和编译期产生的class常量池是有区别的。
5.本地方法区:通过native方式调用的本地方法所划分出来的区域,区别于方法区。
6.直接内存:这部分所使用的不是虚拟机划分出来的内存,而是使用的实际内存。也就是说是jvm指定内存之外的内存。jdk5的NIO通过DirectByteBuffer来分配的缓冲区,就是使用该类型的内存区域。
下面我们谈谈jvm的垃圾回收机制:
首先这部分可以肯定明确的是和堆区有莫大关系。
常用的判断对象废弃的算法:计数算法、根搜索算法。
常用的回收算法有:标记-清除算法、复制算法、标记整理算法、分代收集算法。
标记-清除算法:是最简单的算法也最容易实现。通过标记废弃对象,然后定时触发清除操作。整个过程分为标记、清除两个阶段。
复制算法:是将堆区划分成一个Eden区和两个Survivor区(一个是from 一个是to),其中Eden区较大、2块Survivor区较小。当触法回收时,将会将Eden和一个Survivor区(from)
中还存活的对象一次性copy到另一个Survivor区(to),然后将Eden和Survivor区(from)中的数据清理掉。另外复制算法会导致内存的浪费。如果Survivor区饱和,会通过担保机
制向老生带获取空间。最后一句可以忽略不计。涉及到新生代和老生代将在分代收集中详解。
标记-整理算法:将废弃对象的空出来的内存对齐,然后将多出来的废弃对象所在内存清理掉。该算法运用到老生代。
分代收集算法:分代收集是上面各种算法,根据实例对象在内存中存活特点,将内存区域划代处理。不容易被回收的实例对象经过一次垃圾回收后会移动到老生代的内存,容易被回
收的对象在新生代中暂时保存或者将废弃对象清除。首先在新生代采取“复制算法”,在老生代采取“标记-清理”和“标记-清除”算法。因为这部分数据失效概率小,采用这类方法更合
理高效。
总结:
jvm对于堆区的内存模型如下: