java和c++之间有一堵有动态内存分配和垃圾回收技术所围成的高墙。
1.JVM内存模型
总的来说,java虚拟机的内存区域(运行时数据区域)划分分为以下几个区域:
1,程序计数器(PC)
2,java栈(java虚拟机栈)
3,本地方法栈
4,java堆(gc堆)
5,方法区
其中前三是线程私有(隔离)的,后二者是线程共享的。它们各自具有的作用和特征如下:
1.程序计数器(PC):
——可看作当前线程所执行的字节码的行号指示器。分支,循环,挑战,异常处理,线程恢复等都需要这个计数器来完成
——线程私有
——如果线程执行的是一个java方法,这个计数器记录就是字节码地址;如果线程执行的是Native方法,则为空(Undefined)
——较小的内存空间,唯一没有规定OutOfMemoryError的区域
2.java虚拟机栈:
——描述的是java方法执行的内存区域,每个方法都会创建一个栈帧(Stack Frame)用于存储局部变量表、操组数栈、动态链接和方法出口。
——局部变量表存放基本数据类型(boolean、byte、char、short、int、long、float、double)、对象引用和returnAddress类型。在编译时期完成分配,运行不改变其大小。
——线程私有,与线程生命周期相同
3.本地方法栈:
——与java栈十分相似,是为Native方法服务。有的虚拟机实现的时候会将二者合而为一。
4.java堆:
——虚拟机中管理内存最大的一块。存放对象实例和数组。
——是垃圾回收的主要区域,也叫作GC堆。
——线程共享的java堆中可分出多个线程私有的分配缓冲区(Tread Local Allocation Buffer,TLAB)
5.方法区
——线程共享区域,用于存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。
——逻辑上是java堆的一部分,有个别名“Non-Heap”;
——这部分也是垃圾回收的对象“永久代”,但是效果不好。主要针对常量池的回收和对类型的卸载
——运行时常量池(Runtime Constant Pool)是方法区的一部分,用于存放编译时期生成的各种字面量和符号引用。运行时也可以将新的常量放入池中,String的intern()方法
还有一块不是属于虚拟机运行时数据区的一部分,叫做直接内存(Direct Memory),jdk1.4之后是通过NIO类,引入一种基于通道和缓冲区的I/O方式,通过java堆中的DirectByteBuffer作为这块内存的引用进行操作。
2.垃圾回收原理
1.仅针对java堆和方法区(永久代)的内存区域
2.判断对象是否己死:
——引用计数法:不能解决循环引用的问题
——可达性分析(Reachability Analysis):通过GC Roots对象作为起始点,当一个对象到达GC Roots没有引用链时,表示该对象不可用了。这是现代主流的实现原理
3.当不可达时,处于“缓刑”阶段,至少经历两次标记阶段。一是判断是否有必要执行finalize()方法,如果有必要执行,则放在在F-Queue队列中进行二次标记。如果没有没有覆盖finalize方法或者finalize方法被调用过一次,则说明没有必要执行。
4.回收方法区(永久代)。主要回收废弃常量和无用的类。
5.垃圾回收算法: