JVM内存管理
简介

运行时数据区
1.程序计数器:程序计数器是一块很小的内存空间,是当前线程所执行字节码的行号指示器。线程私有。
2.Java虚拟机栈:同样是线程私有,生命周期和线程一致。这个栈里存放了一些栈帧,程序进入每个方法时会创建一个栈帧,方法执行完毕则出栈。
3.本地方法栈:与虚拟机栈类似,区别是前者服务于执行Java方法,后者服务于本地Native方法。虚拟机规范对于本地方法栈没有强制规定,具体虚拟机可以自由实现它,Sun HotSpot虚拟机就把本地方法栈和虚拟机栈合二为一了,共用一个也是可以的。
本地方法:是遵循JNI(Java Native Interface)的任何非Java语言实现的方法,一般是与硬件、操作系统交互。
4.Java堆:Java堆是最大的一块区域,所有对象实例、集合、数组都存在此处。(随着JIT编译器的发展,逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术导致一些微妙变化,并不是所有对象都放在堆上)
Java堆可分为:新生代、老年代。
新生代包括:Eden区、From Survivor区、To Survivor区
老年代只是老年代。
5.方法区:方法区和Java堆一样,是多个线程共享的区域。存储类信息、常量、静态变量、即时编译器编译后的代码等。方法区也叫Non-Heap(非堆)。
运行时常量池:这个池是方法区的一部分,主要存放上述的常量。
字符串常量池JDK1.6之前是位于方法区中的常量池中,JDK1.7时已经被挪到堆之中。
永久代:有人把方法区也称作永久代,实际上二者并不等价。仅仅是因为HotSpot虚拟机选择把GC分代收集扩展至方法区,这样可以像管理Java堆一样管理方法区。省去专门为方法区编写内存管理代码的工作。
6.直接内存:直接内存时由Native函数库直接分配的,一般在使用文件IO时会用到。
HotSpot虚拟机对象探秘(创建,内存布局,访问定位)
对象的创建:
首先检查new指令的参数(也就是类名)能否在常量池中定位到一个类的符号引用,并且检查该类是否已加载、解析、初始化过。如果没有那就要现执行类加载过程。
检查通过后,开始分配内存。实例所需内存大小在类加载完成后可完全确定(TODO 确定方法在2.3.2小节)。
两种内存分配方式,JVM会选择哪一种由垃圾收集器决定。
(1)指针碰撞(Dump the pointer)
当Java堆中内存是绝对规整的,所有在用的内存都放在一边,空闲的放在另一边,中间有一个指针作为分界点,那所分配的内存就仅仅是把那个指针向空闲的内存挪动一段与对象大小相等的距离。
(2)空闲列表(Free list)
如果Java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,那就不能简单的进行指针
带有压缩整理功能的垃圾收集器,如Serial、ParNew等带有Compact过程的收集器,系统采用的分配算法是指针碰撞。
没有压缩整理功能的垃圾收集器CMS,它是基于Mark-Sweep算法的收集器。通常采用的分配算法是空闲列表。