【快速入门JVM】Java虚拟机规范之Java体系结构概述
JVM是运行在操作系统之上的,它与硬件没有直接的交互,它主要包括运行时数据区:方法区+堆+Java栈+本地方法栈+程序计数器,以下就各自的区别进行了详细的描述。
一、类装载器一 一 Classloader
主要负载加载class
文件的加载(硬盘挪到内存,变成大Class
,做元数据模板),它是否运行,由执行引擎(Execution Engine
)负责解释命令,提交给操作系统执行!
Java虚拟机采用双亲委派模式即把请求交由父类处理,它一种任务委派模式!
- 启动类加载器(
Bootsrap
):C++语言实现; - 扩展类加载器(
Extension
):Java语言实现; - 应用程序加载器(
AppClassLoader
):Java语言实现,又称系统类加载器,加载当前应用的classpath
的所有类; - 用户自定义加载器:
Java.lang.ClassLoader
的子类,用户可定制类的加载方式。
二、本地方法接口一 一 Native Interface
本地方法接口主要是为了融合不同的编程语言为Java所用,而Java出现的比较迟,所以要想立足,必须要调用C/C++
程序,于是就在内存中开辟了一块区域处理标记的为Native的代码,它具体的做法就是Native Method Stack
中登记native
方法,在执行引擎执行时加载native libraies
。
目前这种方法应用的比较少,一般是硬件的应用,如通过Java程序驱动打印机或Java系统管理生产设备。
三、本地方法栈一 一Native Method Stack
Native Method Stack
中登记native
方法,在Execution Engine
执行时加载本地方法栈。
四、程序计数器/PC寄存器一 一 Program Counter Register
每个线程都有一个程序计数器,是线程私有的,是一个指针,指向方法区中的方法字节码,有执行引擎读取下一条指针(线程结束,指针就是随之消失,不需要GC,占用内存小)
五、方法区一 一 Method Area
方法区是被所有线程共享的,所有字段和方法字节码,一些特殊方法如构造函数,接口代码也再次定义。其生命周期很长。
方法区包括了静态变量(类变量)+常量+类信息(构造方法/接口定义)+运行时常量池存在方法区中,new的对象存在堆内存中,和方法区无关。
六、Java栈一 一 Java Stack
(官方)软件工程=数据结构+算法,(工作)软件工程=业务需求+框架
队列:先进先出,栈:先进后出
栈也是栈内存,主管Java程序的线程,是线程创建时创建,它是生命周期跟随线程的生命周期,线程结束栈内存也就释放了,对于栈来说,不存在垃圾回收问题!生命周期和线程一致,是线程私有的。
栈包括8中基本数据类型+对象的引用变量+实例方法都是在函数的栈内存分配。
栈帧存储主要保存3类数据
-
本地变量(
Local Variables
):输入参数和输出参数以及方法内的变量; -
栈操作(
Operand Stack
):记录出栈、入栈的操作; -
栈帧数据(
Frame Data
):包括类文件、方法等等。
Java栈遵循“先进后出”、“后进先出”的原则。
每执行的方法都会产生一个栈帧,保存到栈的顶部,顶部栈就是当前的方法,该方法执行完毕后自动将此栈帧出栈。栈内存异常“
java.lang.StackOverflowError
”问题,需要避免循环递归调用的场景!
栈&堆&方法区交互关系
HotSpot
(JDK商标):使用指针的方式来访问对象;Java堆中会存放类元数据的地址,reference存储的就是接是对象的地址。
-
Sun
公司的HotSport
-
BEA
公司的JRockit
-
IBM
公司的J9 VM
七、堆一 一Heap
一个JVM实例只存在一个堆内存,堆的大小是可以调节的。类加载器读取类文件后,需要把类、方法、常变量放到内存中,保存所有引用类型的真实信息,方便执行器执行。
堆内存再逻辑上分为三个部分:新生+养老+永久。
GC发生在新生区,默认经过15次GC,才会进去养老区,一直使用的对象才会存放在养老区,比如池对象、线程对象,养老区也有GC,叫做Full GC
(全面GC)!养老区与永久区是不通的,养老区快塞满时,Full GC也无法回收时,就报OOM
(内存溢出)!
新生区又分为两个部分:伊甸区(Eden space
)和幸存区(Survivor space
),所有的类都是在伊甸区被new
出来。
幸存区又分为两部分:0区(Survivor 0 space
)和1区(Survivor 1 space
)。
当伊甸区的空间用完后,程序还需创建对象,JVM的垃圾回收器将对伊甸区进行垃圾回收(Minor GC
),将伊甸区中不再被其它对象所引用的对象进行销毁,然后将伊甸区中剩余的对象移动到幸存0区,假如幸存0区已满,再对对该区进行垃圾回收,然后移动到1区,如果1区也满了,再次进行垃圾回收,满足条件后移动到养老区,假如养老区也满了,那么这时就会产生MajorGC(Full GC)
,进行养老区的内存清理。如果养老区执行了Full GC
后发现依然无法进行对象的保存,就会产生OOM
异常“OutOfMenoryError
”。
抛异常“java.lang.OutOfMemoryError:java heap space
”异常,说明JVM的堆内存不够,可能有以下原因:
- JVM的堆内存设置不够,可以通过
-Xms、-Xmx
来调整 - 代码中创建了大量的对象,并且长时间不能被垃圾收集器收集(存在被引用)
Jdk1.6及之前: 有永久代, 常量池1.6在方法区
Jdk1.7: 有永久代,但已经逐步“去永久代”,常量池1.7在堆
Jdk1.8及之后: 无永久代,常量池1.8在元空间
☝上述分享来源个人总结,如果分享对您有帮忙,希望您积极转载;如果您有不同的见解,希望您积极留言,让我们一起探讨,您的鼓励将是我前进道路上一份助力,非常感谢!我会不定时更新相关技术动态,同时我也会不断完善自己,提升技术,希望与君同成长同进步!
☞本人博客:https://coding0110lin.blog.****.net/ 欢迎转载,一起技术交流吧!