JVM内存管理
JVM在执行java程序的过程中,会把内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。
程序计数器
程序计数器:当前线程所执行字节码的行号指示器。
由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。为了线程切换后能够恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各线程之间计数器互不影响,独立存储。
程序计数器:线程私有的内存。
Java虚拟机栈
每个方法在执行的同时,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
Java虚拟机栈,与程序计数器一样,是线程私有的,生命周期同线程相同。
本地方法栈
本地方法栈类似于虚拟机栈,区别在于:虚拟机栈为虚拟机执行java方法服务。
本地方法栈则为虚拟机执行Native方法服务。
Java堆
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
Java堆的唯一目的,就是存放对象实例,几乎所有的对象实例都在这里分配内存。
即:所有的对象实例及数组,都要在堆中分配内存。
Java堆分为新生代、旧生代。
新生代又分为:Eden区、From Survivor区、To Survivor区。目前,收集器基本都采用分代收集算法,进一步划分内存空间的目的,是为了更好地回收内存或者更快的分配内存。
方法区
方法区用于存储类信息、常量、静态变量、及时编译器编译后的代码等数据。
垃圾收集器
程序计数器、虚拟机栈、本地方法栈三个区域随线程而生,随线程而灭。
栈中的栈帧随着方法的进入和退出而有条不紊地执行着入栈和出栈的操作。每个栈帧中分配多少内存基本上在类结构确定下来时就已知。
这几个区域的内存分配和回收都具备确定性,在这几个区域内,就不需要过多考虑回收的问题。因为方法结束或线程结束时,内存自然就跟着回收了。
内存分配
对象的内存分配,往大方向讲,就是堆上分配。
对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下,也可能会直接分配在老年代。
新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为java对象大多数都具备朝生夕灭的特征。
所以,Minor GC非常频繁,一般回收速度也比较快。
老年代GC(Major GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC。
Major GC的速度一般会比Minor GC速度慢10倍以上。
分代收集算法:
在新生代中,每次垃圾收集时,都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本,就可以完成收集。
年老代中,因为对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记-清理”,“标记-整理”算法来进行回收。