面试必问的JVM知识-JVM特性学习( 二 )


方法区方法区(Method Area)与Java堆一样 , 是各个线程共享的内存区域 , 它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据 。
JDK 7的HotSpot , 已经把原本放在永久代的字符串常量池、静态变量等移出 , 而到了 JDK 8 , 终于完全废弃了永久代的概念 , 改用与JRockit、J9一样在本地内存中实现的元空间(Metaspace)来代替 , 把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中 。
注意上面这段话 , JDK8已经没有永久代了 , 而是使用了元空间 。 JDK7时 , 很多人喜欢将方法区和永久代混为一谈 。
垃圾收集行为在这个区域的确是比较少出现的 , 但并非数据进入了方法区就如永久代的名字一样"永久"存在了 。 这区域的内存回收目标主要是针对常量池的回收和对类型的卸载 。 如果方法区无法满足新的内存分配需求时 , 将抛出OutOfMemoryError异常 。
运行时常量池运行时常量池(Runtime Constant Pool)是方法区的一部分 。 Class文件中除了有类的版本、字段、方法、接口等描述信息外 , 还有一项信息是常量池表(Constant Pool Table) , 用于存放编译期生成的各种字面量与符号引用 , 这部分内容将在类加载后存放到方法区的运行时常量池中 。
运行时常量池是方法区的一部分 , 当常量池无法再申请到内存时会抛出OutOfMemoryError异常 。
直接内存直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分 , 但是这部分内存也被频繁地使用 , 而且也可能导致OutOfMemoryError异常出现 。
对象在HotSpot虚拟机里 , 对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例 数据(Instance Data)和对齐填充(Padding) 。
HotSpot虚拟机对象的对象头部分包括两类信息 。 第一类是用于存储对象自身的运行时数据 , 也叫"Mark Word" 。 "Mark Word"信息如下:
面试必问的JVM知识-JVM特性学习文章插图
Mark Word
我们的Java程序会通过栈上的reference数据来操作堆上的具体对象 。 主流的访问方式主要有使用句柄和直接指针两种 。
句柄访问:Java堆中将可能会划分出一块内存来作为句柄池 , reference中存储的就是对象的句柄地址 , 而句柄中包含了对象实例数据与类型数据各自具体的地址信息
直接指针访问: , Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息 , reference中存储的直接就是对象地址 , 如果只是访问对象本身的话 , 就不需要多一次间接访问的开销
一般HotSpot虚拟机使用的是第二种直接指针访问 。
面试必问的JVM知识-JVM特性学习文章插图
直接指针
这里我提出一个问题 , 大家可以思考:
如何写出堆溢出异常和栈溢出异常的代码?
【面试必问的JVM知识-JVM特性学习】这个是面试常被问到的问题 。 关于堆溢出异常 , 可以从创建对象 , 让对象撑爆堆内存方面思考;关于栈溢出异常 , 可以考虑如何让栈帧塞满栈 。