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