###如图:
-
类加载器
类加载器(Class loader)负责把class文件加载进运行时数据区。
类加载器有三种:
1.启动类加载器(Bootstrap),$JAVAHOME/jre/lib/rt.jar;
2.扩展类加载器(Extension),$JAVAHOME/jre/lib/ext/*.jar;
3.应用程序类加载器(AppClassLoader);
它们遵循双亲委派机制。当一个类收到了类加载请求,会把请求委派给父类加载,每一个层次类加载器都是如此,只有当父类加载器反馈不能加载时(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。这样就保证了使用不同的类加载器最终得到的都是同一对象。 -
运行时数据空间
由五部分组成:程序计数器、本地方法栈、栈、堆、方法区。其中堆和方法区是线程共享的,其他是线程独享、
-
程序计数器
程序计数器就是一条指针,指向方法区的方法字节码,由执行引擎(Execution Engine)读取下一条指令。执行引擎负责解释命令,提交操作系统执行。
-
本地方法栈
本地方法栈(Native method stack)会调用本地接口(Native Interface)实现线程的创建。
-
栈内存
栈主管Java程序的运行,在线程创建时创建,生命期是跟随线程的生命期,线程结束栈内存也就释放。
栈主要保存三类数据:
1.本地变量(Local Variables):输入参数和输出参数以及方法内的变量(8种基本数据类型+对象的引用变量);
2.栈操作(Operand Stack):记录出栈、入栈的操作;
3.栈帧数据(Frame Data):包括类文件、实例方法等。
栈运行遵循“先进后出”原则。
栈的大小和具体JVM的实现有关,通常在256K~756K之间。
常见异常:java.lang.StackOverflowError
(1)栈的小格子不够了,不是因为内存溢出!
(2)调用了太多的方法,导致栈溢出。比如死循环递归调用。 -
堆内存
堆存储对象的实例。堆分为新生代和老年代。Hot Spot JVM把年轻代分为了三部分,一个Eden和两个survivor(From和To),默认是8:1:1。Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,设置两个 Survivor 区最大的好处就是解决内存碎片化。
常见异常:java.lang.OutOfMemoryError: Java heap space
(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集。 -
方法区
存储静态变量、常量、类结构(构造方法/接口定义)、运行时常量池。
运行时常量池:虚拟机会将各个class文件中的常量池载入到运行时常量池中,即编译期间生成的字面量、符号引用,总之就是装载class文件。
方法区是Java虚拟机规范中的定义,是一种规范。
对于Java8,元空间(MateSpace)是方法区的实现,元空间并不在虚拟机中而是使用本机物理内存。
但静态变量和常量池并不存入元空间,而是存储在堆中。
字符串常量池也储存在堆中。
栈、堆、方法区的交互关系
栈中的引用对象指向堆内存中对象的实例数据,堆内存实例数据是根据方法区类结构产生。