运行时数据区结构图
堆、栈、方法区的交互关系
html
《Java虚拟机规范》中明确说明:‘尽管全部的方法区在逻辑上属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。’但对于HotSpotJVM而言,方法区还有一个别名叫作Non-heap(非堆),目的就是要和堆分开。
因此,==方法区能够看做是一块独立于Java堆的内存空间。==java
public class MethodAreaDemo {
public static void main(String[] args) {
System.out.println("start...");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end...");
}
}
复制代码
方法区在jdk7及jdk8的落地实现 git
在jdk8中,终于彻底废弃了永久代的概念,改用与JRockit、J9同样在本地内存中实现的元空间(Metaspace)来代替
github
元空间的本质和永久代相似,都是对JVM规范中方法区的实现。不过元空间与永久代最大的区别在于:==元空间再也不虚拟机设置的内存中,而是使用本地内存==面试
永久代、元空间并不仅是名字变了。内部结构也调整了算法
根据《Java虚拟机规范》得规定,若是方法区没法知足新的内存分配需求时,将抛出OOM异常.数据库
方法区的大小没必要是固定的,jvm能够根据应用的须要动态调整。
jdk7及之前:编程
jdk8及之后:windows
* jdk7及之前:
* 查询 jps -> jinfo -flag PermSize [进程id]
* -XX:PermSize=100m -XX:MaxPermSize=100m
*
* jdk8及之后:
* 查询 jps -> jinfo -flag MetaspaceSize [进程id]
* -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m
复制代码
如下代码在JDK8环境下会报 Exception in thread "main" java.lang.OutOfMemoryError: Compressed class space 错误
数组
/**
* jdk6/7中:
* -XX:PermSize=10m -XX:MaxPermSize=10m
*
* jdk8中:
* -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
*
*/
public class OOMTest extends ClassLoader {
public static void main(String[] args) {
int j = 0;
try {
OOMTest test = new OOMTest();
for (int i = 0; i < 10000; i++) {
//建立ClassWriter对象,用于生成类的二进制字节码
ClassWriter classWriter = new ClassWriter(0);
//指明版本号,修饰符,类名,包名,父类,接口
classWriter.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
//返回byte[]
byte[] code = classWriter.toByteArray();
//类的加载
test.defineClass("Class" + i, code, 0, code.length);//Class对象
j++;
}
} finally {
System.out.println(j);
}
}
}
复制代码
对每一个加载的类型( 类class、接口interface、枚举enum、注解annotation),JVM必 .须在方法区中存储如下类型信息:
JVM必须保存全部方法的如下信息,同域信息同样包括声明顺序:
如下代码不会报空指针异常
public class MethodAreaTest {
public static void main(String[] args) {
Order order = null;
order.hello();
System.out.println(order.count);
}
}
class Order {
public static int count = 1;
public static final int number = 2;
public static void hello() {
System.out.println("hello!");
}
}
复制代码
全局常量 static final 被声明为final的类变量的处理方法则不一样,每一个全局常量在编译的时候就被分配了。
代码解析
Order.class字节码文件,右键Open in Teminal打开控制台,使用javap -v -p Order.class > tst.txt 将字节码文件反编译并输出为txt文件,能够看到==被声明为static final的常量number在编译的时候就被赋值了,这不一样于没有被final修饰的static变量count是在类加载的准备阶段被赋值==
public static int count;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC
public static final int number;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: int 2
复制代码
Public class Simpleclass {
public void sayhelloo() {
System.out.Println (hello) }
}
复制代码
几种在常量池内存储的数据类型包括:
常量池,能够看作是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型、字面量等信息。
public class MethodAreaDemo {
public static void main(String[] args) {
int x = 500;
int y = 100;
int a = x / y;
int b = 50;
System.out.println(a + b);
}
}
复制代码
main方法的字节码指令
0 sipush 500
3 istore_1
4 bipush 100
6 istore_2
7 iload_1
8 iload_2
9 idiv
10 istore_3
11 bipush 50
13 istore 4
15 getstatic #2 <java/lang/System.out>
18 iload_3
19 iload 4
21 iadd
22 invokevirtual #3 <java/io/PrintStream.println>
25 return
复制代码
"Exception in thread' dubbo client x.x connector’java.lang.OutOfMemoryError: PermGenspace"
而元空间和永久代之间最大的区别在于:==元空间并不在虚拟机中,而是使用本地内存。所以,默认状况下,元空间的大小仅受本地内存限制==。jdk7中将StringTable放到了堆空间中。由于永久代的回收效率很低,在full gc的时候才会触发。而full GC 是老年代的空间不足、永久代不足时才会触发。这就致使了StringTable回收效率不高。而咱们开发中会有大量的字符串被建立,回收效率低,致使永久代内存不足。放到堆里,能及时回收内存.
/**
* 《深刻理解Java虚拟机》中的案例:
* staticObj、instanceObj、localObj存放在哪里?
*/
public class StaticObjTest {
static class Test {
static ObjectHolder staticObj = new ObjectHolder();
ObjectHolder instanceObj = new ObjectHolder();
void foo() {
ObjectHolder localObj = new ObjectHolder();
System.out.println("done");
}
}
private static class ObjectHolder {
}
public static void main(String[] args) {
Test test = new StaticObjTest.Test();
test.foo();
}
}
复制代码
hsdb>scanoops 0x00007f32c7800000 0x00007f32c7b50000 JHSDB_ _TestCase$Obj ectHolder
0x00007f32c7a7c458 JHSDB_ TestCase$Obj ectHolder
0x00007f32c7a7c480 JHSDB_ TestCase$Obj ectHolder
0x00007f32c7a7c490 JHSDB_ TestCase$Obj ectHolder
复制代码
有些人认为方法区(如Hotspot,虚拟机中的元空间或者永久代)是没有垃圾收集行为的,其实否则。《Java 虚拟机规范》对方法区的约束是很是宽松的,提到过能够不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如 JDK11 时期的 2GC 收集器就不支持类卸载)。
通常来讲这个区域的回收效果比较难使人满意,尤为是类型的卸载,条件至关苛刻。可是这部分区域的回收有时又确实是必要的。之前 Sun 公司的 Bug 列表中,曾出现过的若干个严重的 Bug 就是因为低版本的 Hotspot 虚拟机对此区域未彻底回收而致使内存泄漏。
方法区的垃圾收集主要回收两部份内容:常量池中废奔的常量和再也不使用的类型
三面:说一下JVM内存模型吧,有哪些区?分别干什么的?
Java8的内存分代改进
JVM内存分哪几个区,每一个区的做用是什么?
一面: JVM内存分布/内存结构?栈和堆的区别?堆的结构?为何两个survivor区?
二面: Eden和Survior的比例分配
jvm内存分区,为何要有新生代和老年代
二面: Java的内存分区
二面:讲讲jvm运行时数据库区
何时对象会进入老年代?
JVM的内存结构,Eden和Survivor比例 。
JVM内存为何要分红新生代,老年代,持久代。新生代中为何要分为Eden和Survivor。
一面: Jvm内存模型以及分区,须要详细到每一个区放什么。
一面: JVM的内存模型,Java8作了什么修改
JVM内存分哪几个区,每一个区的做用是什么?
java内存分配
jvm的永久代中会发生垃圾回收吗?
一面: jvm内存分区,为何要有新生代和老年代?
【代码】
github.com/willShuhuan…
【笔记】
JVM_01 简介
JVM_02 类加载子系统
JVM_03 运行时数据区1- [程序计数器+虚拟机栈+本地方法栈]
JVM_04 本地方法接口
JVM_05 运行时数据区2-堆
JVM_06 运行时数据区3-方法区
JVM_07 运行时数据区4-对象的实例化内存布局与访问定位+直接内存
JVM_08 执行引擎(Execution Engine)
JVM_09 字符串常量池StringTable
JVM_10 垃圾回收1-概述+相关算法
JVM_11 垃圾回收2-垃圾回收相关概念
JVM_12 垃圾回收3-垃圾回收器