JVM内存管理-
线程
JVM运行实际程序的实体是线程,线程存储必要数据的内存空间为堆栈,每个线程创建时候,JVM会为它创建一个堆栈,堆栈的大小与JVM的实现有关。
JAVA堆
Java堆是用于存储java对象的内存区域,堆的大小再jvm启动时向操作系统申请一次(根据配置参数),一旦分配成功,对的大小将会固定,不能再内存不够时再向系统申请。java堆中的内存管理由JVM控制,对象创建由java应用程序控制,但是对象所占的空间释放由管理堆内存的垃圾收集器完成。
每一个存储在堆中的Java对象都会是这这个对象的副本,这个副本包括继承自它父类的所有非静态属性。
堆时所有Java线程所共享的,所以对他的访问需要注意同步问题。
方法区
属于堆的一部分(堆中的永久区),存储类结构信息的地方(常量池、域、方法数据、方法体、构造函数、专用方法、实例初始化、接口初始化)。该区域线程共享,大小可以通过参数设置。
方法区存储的信息相对比较稳定,不会频繁的被GC回收器回收。
运行时常量池
方法区的一部分
JAVA栈
java的栈跟线程相关联,每当创建一个线程时,jvm会为这个线程创建一个java栈,在这个java栈中又会有多个栈帧,栈帧与每个方法关联起来,每运行一个方法就创建一个栈帧,每个栈帧会含有一些在方法内定义的变量、操作栈和方法返回值等信息。
栈帧在方法执行完成时弹出栈中元素作为返回值,并消除这个栈,所以java栈的栈顶就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向这个地址。
操作栈只能使用当前方法的本地变量,当在一个栈桢中调用另外一个方法时,与之对应的一个新的栈桢又被创建,这个新创建的栈帧又被放到Java栈顶,变为当前的活动栈桢。
java栈与线程对应,所以栈中数据不是线程共享的。
内存分配策略
JVM内存分配主要基于堆、栈。(操作系统的分配策略分为静态内存分配以及栈、堆内存分配)
栈
栈的分配和线程绑定在一起,创建一个线程时,便会创建一个栈,线程方法的调用、返回对应栈的压栈和出栈。栈桢用于保存参数、局部变量、中间计算过程和其它数据(主要还是为基本类型的变量以及对象的引用),存取速度块,仅次于寄存器。
堆
所有对象或数组的存储空间都是在堆中分配的(对象的引用存储空间在栈中),每个java应用对应一个JVM实例,每个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由运用程序所有的线程共享。
堆内存是自动初始化的,java的堆是一个运行时数据区,运行时动态分配内存,由GC回收器负责空间的释放。
堆由于在分配、销毁内存时候需要经过操作系统,期间占用的时间导致用堆的效率非常低。堆的优点在于不必知道所需的存储空间的大小以及存活时长,因此堆更加的方便。除此之外,由于面向对象编程的多态性,多态变量所需的存储空间只有在运行时创建了对象之后才能确定。
内存回收策略
静态内存回收
在java的类和方法中的局部变量包括原生数据类型和对象的引用都是静态分配内存的,这些数据的大小固定且内存不会在程序执行时发生变化,所以其总内存总需求在java被编译时便能够确定下来,在程序被加载时系统便把该部分的内存一次性的分配完,直到程序执行结束时,这部分内存才会被回收。
动态内存回收
Java中的对象的内存空间,只有在程序执行过程中,JVM解析了类后才能知道该对象所属的类有哪些信息,然后才能为这些信息分配相应的存储空间存储相应的值。此类空间在对象不再使用时被回收,即是说以对象不再被引用为前提。
垃圾识别
JVM检查堆中所有对象是否都会被活动对象变量直接或者间接引用,不被活动对象变量引用的对象便是垃圾。
活动对象变量:
- 方法中局部变量区的对象变量:这些对象变量存储在栈桢的局部变量区
- java操作栈中的对象变量:某些对象在操作栈中持有
- 常量池中的对象变量:这个指的是类的实例域中包含的对象变量
- 本地方法中持有的对象变量:有些对象被传入本地方法中,但是这些对象还没有被释放
- 类的Class对象变量:每个类被JVM加载时都会创建一个代表这个类的唯一数据类型的Class对象,这个对象存放在堆中,当这个类不再被使用时,方法区中的类数据和这个对象需要被回收
垃圾收集算法参考
JVM内存相关部分总结
大多数 JVM 将内存区域划分为 Method Area(Non-Heap)(方法区) ,Heap(堆) , Program Counter Register(程序计数器) , VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的),Native Method Stack ( 本地方法栈 )。
其中Method Area方法区和Heap堆是线程共享的,VM Stack,Native Method Stack 和Program Counter Register 是非线程共享的。为什么分为 线程共享和非线程共享的呢?请继续往下看。
首先我们熟悉一下一个一般性的 Java 程序的工作过程。一个 Java 源程序文件,会被编译为字节码文件(以 class 为扩展名),每个java程序都需要运行在自己的JVM上,然后告知 JVM 程序的运行入口,再被 JVM 通过字节码解释器加载运行。那么程序开始运行后,都是如何涉及到各内存区域的呢?
概括地说来,JVM初始运行的时候都会分配好 Method Area(方法区) 和Heap(堆) ,而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)和Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说知发生在Heap上)的原因