JVM

JVM线程共享内存区域

Posted by Clear Blog on May 21, 2017

Java堆

虚拟机中管理内存最大的一块,唯一的功能就是存放对象实例。 该区域是垃圾收集器管理的主要区域,很多时候被称为GC堆. 堆内存垃圾收集器采用的是分代收集算法,Java堆中可细分为新生代和老年代, 再细致一点新生代可分为Eden空间,From Survivor空间和To Survivor空间。 可通过-Xmx和-Xms控制堆内存的最大最小内存值,将两个值设为一样可避免自动扩展, 内存变化的会导致JVM消耗额外的资源和性能来缩小或者开辟内存。 可以更细致的去划分堆内存空间,通过-Xmn来设置堆内存内年轻代内存。 因为在jvm中如果老年代将满的时候会发生full gc,会停顿所有用户线程,所以一般情况下分配的新生代和老年代内存 内存比例是1/3到1/4。可以通过-XX:NewRatio=老年代/新生代 参数来配置比例。 在新生代中还可以通过-XX:SurvivorRatio来分配Eden区和Survivor区的比例。 这里的Survivor区指的是两块中的任意一块。 比如说新生代内存为10m,-XX:SurvivorRatio=2,那么内存分配就是10/2+1+1的大小去给各个区域分配。 如果堆中没有内存完成实例分配,并且堆也无法再扩展时,将抛出OutOfMemoryError异常。

方法区

别名Non-Heap(非堆),很多人把方法去称为永久代(Permanent Generation),用于存放类信息,常量,静态变量,即时编译器编译后的代码。 常量池是方法去的一部分,用于存放编译期生成的各种字面量和符号引用。 -XX:PermSize设置永久带最小内存,-XX:MaxPermSize设置永久代最大内存。我觉得这块的参数直接交给jvm处理就可以, 它会配置一个合理的大小。

字符串常量池、class常量池和运行时常量池

全局字符串池(string pool)

该常量池中的内容是在jvm中类完成验证和准备阶段之后,经过验证,准备阶段之后在堆中生成字符串对象实例, 然后将该字符串对象实例的引用值存到string pool中(记住:string pool中存的是引用值而不是具体的实例对象, 具体的实例对象是在堆中开辟的一块空间存放的。

class文件常量池(class constant pool)

在Class文件中存在ConstantPool,其中存放的就是用final声明的常量以及符号引用,符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量, 只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)

运行时常量池(runtime constant pool)

jvm在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。 而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。

class常量池中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。 而经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询全局字符串池, 也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与全局字符串池中所引用的是一致的。

  1. 全局常量池在每个VM中只有一份,存放的是字符串常量的引用值。
  2. class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量的符号引用。
  3. 运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说, 每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。