【快速入门JVM】Java虚拟机规范之Java体系结构概述

  JVM是运行在操作系统之上的,它与硬件没有直接的交互,它主要包括运行时数据区:方法区+堆+Java栈+本地方法栈+程序计数器,以下就各自的区别进行了详细的描述。

【快速入门JVM】Java虚拟机规范之Java体系结构概述_第1张图片
一、类装载器一 一 Classloader

类装载器为JVM的入口,执行引擎为JVM的出口–把JVM翻译给操作系统。

  主要负载加载class文件的加载(硬盘挪到内存,变成大Class,做元数据模板),它是否运行,由执行引擎(Execution Engine)负责解释命令,提交给操作系统执行!

【快速入门JVM】Java虚拟机规范之Java体系结构概述_第2张图片

类装载器四大类型

Java虚拟机采用双亲委派模式即把请求交由父类处理,它一种任务委派模式!

  • 启动类加载器(Bootsrap):C++语言实现;
  • 扩展类加载器(Extension):Java语言实现;
  • 应用程序加载器(AppClassLoader):Java语言实现,又称系统类加载器,加载当前应用的classpath的所有类;
  • 用户自定义加载器:Java.lang.ClassLoader的子类,用户可定制类的加载方式。
【快速入门JVM】Java虚拟机规范之Java体系结构概述_第3张图片
二、本地方法接口一 一 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 -- 栈内存溢出”问题,需要避免循环递归调用(一致压栈、一直不出栈)的场景!

【快速入门JVM】Java虚拟机规范之Java体系结构概述_第4张图片

栈&堆&方法区交互关系

HotSpot(JDK商标):使用指针的方式来访问对象;Java堆中会存放类元数据的地址,reference存储的就是接是对象的地址。

【快速入门JVM】Java虚拟机规范之Java体系结构概述_第5张图片

熟知的3种JVM类型

  • Sun公司的HotSport(jdk的商标)
  • BEA公司的JRockit
  • IBM公司的J9 VM
七、堆一 一Heap

  一个JVM实例只存在一个堆内存,堆的大小是可以调节的。类加载器读取类文件后,需要把类、方法、常变量放到内存中,保存所有引用类型的真实信息,方便执行器执行。

  堆内存再逻辑上分为三个部分:新生+养老+永久。

  GC发生在新生区,默认经过15次GC,才会进去养老区,一直使用的对象才会存放在养老区,比如池对象、线程对象,养老区也有GC,叫做Full GC(全面GC)!养老区与永久区是不通的,养老区快塞满时,Full GC也无法回收时,就报OOM(内存溢出)!

【快速入门JVM】Java虚拟机规范之Java体系结构概述_第6张图片
  新生区是类的诞生、成长、消亡的区域,一个类在这里产生、应用。最后被垃圾回收器收集,结束生命。

  新生区又分为两个部分:伊甸区(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的堆内存不够,可能有以下原因:

  • 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/  欢迎转载,一起技术交流吧!

你可能感兴趣的:(JUC&JVM)