一、JVM体系结构概述

  • JVM位置

  • JVM体系结构

1.1 类加载器 ClassLoader

  类加载器(ClassLoader)负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。

大致流程图如下图所示:

1.1.1 类加载器的分类

  在java中类加载器共分为两大类,分别是:虚拟机自带的加载器用户自定义加载器

其中:

虚拟机自带的加载器

  • 启动类加载器(Bootstrap)C++

  • 扩展类加载器(Extension)Java

  • 应用程序类加载器(AppClassLoader)Java(也叫系统类加载器,加载当前应用的classpath的所有类)

用户自定义加载器

  • Java.lang.ClassLoader的子类,用户可以定制类的加载方式

1.1.2 类加载器的加载过程

  类加载器对于类的加载有两个机制,分别是:双亲委派机制和砂箱安全机制。

  类加载器加载类的过程如图所示:

  从图中我们可以看出,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理会先检查自己是否已经加载过,如果没有再往上。注意这个过程,直到到达Bootstrap classLoader之前,都是没有哪个加载器自己选择加载的。如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

1.2 本地方法栈

  至于jvm中的本地方法栈,该方法栈是针对native方法来进行压栈的。具体过程为:

  执行native方法的时候需要将native方法压入到本地方法栈,本地方法栈中的方法就需要操作系统来执行,这个时候需要执行引擎Execution Engine 来负责进行解释,然后调用执行本地方法接口(这时会用到本地方法库)。

1.3 程序计数器

程序计数器就是一个小小的指针(是一个非常小的内存空间,几乎可以忽略不记),通过它来确定执行完一个方法之后再执行下一个方法。其所处位置类似于两节车厢的连接处。

1.4 Execution Engine

Execution Engine执行引擎负责解释命令,提交操作系统执行。

1.5 方法区 Method Area

  方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此区属于共享区间

静态变量 + 常量 + 类信息(构造方法/接口定义) + 运行时常量池存在方法区中

  但是一定要注意:实例变量存在于堆内存中,和方法区无关。

1.6 栈 Stack

1.6.1 栈是什么

  栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配。

1.6.2 栈运行原理

  栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧 F1,并被压入到栈中,

  A方法又调用了 B方法,于是产生栈帧 F2 也被压入栈,

  B方法又调用了 C方法,于是产生栈帧 F3 也被压入栈,

  ……

  执行完毕后,先弹出F3栈帧,再弹出F2栈帧,再弹出F1栈帧……

  遵循“先进后出”/“后进先出”原则。

1.7 堆(Heap)体系结构

  堆内具体划分得看从逻辑上划分还是从物理上划分。

1.7.1 从逻辑上划分

  堆从逻辑上分为新生区、养老区和永久区

  其中新生区又分为伊甸园区幸存者区,幸存区分为幸存0区(from区)幸存1区(to区)

  大致结构图如下图所示:

  new的对象首先被放到伊甸园区,如果经过垃圾回收这个对象没有被回收掉,那么会把它移动到幸存0区,

  如果再次经过垃圾回收这个对象仍然没有被回收掉,那么这次会把它移动到幸存1区,再垃圾回收,仍然幸存的话,就移动到养老区。

  在养老区之后,就不会被minor GC垃圾回收。

1.7.2 垃圾回收 GC

  垃圾回收分为minor GC 和full GC,其中minor GC针对 Eden区和from区,full GC针对新生区和养老区。

垃圾回收过程:

新new的对象在伊甸园区(Eden)出生,随着对象越来越多,如果要是放不下的话,会触发轻量级的垃圾回收(minor GC),minor GC会回收Eden区和from区。

  针对Eden区,minor GC垃圾回收后,会将Eden中的幸存者移到to区。

  针对from区,minor GC垃圾回收后,会根据from区中的对象的age来先进行一个判断,如果from中的对象的age<15那么会将该对象移动到to区,如果age>=15就会将该对象移动到养老区,这些对象的age,每移动一次,其age就会加1。

这个时候,Eden区中的幸存对象移动到了to区,from区中age<15的也移动到了to区,然后把Eden区清空,from区也清空,

  然后from和to区交换位置,即from区变成了to区,to区变成了from区。因此:to总为空。

如此不断循环。

特殊情况:如果new的对象比较大,伊甸园区放不下的话,就会在养老区创建,如果养老区空间也不够的话,直接就报oom了。

经过不断的minor GC,养老区中的对象会越来越多,如果养老区空间放不下的话,就会触发Full GC,Full GC对新生区和养老区都会进行回收,如果Full GC也回收不了内存的话,就会出现OOM。

产生OOM有两个原因:

(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。

(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。

1.7.3 永久区

  永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。

  如果出现java.lang.OutOfMemoryError: PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。或者大量动态反射生成的类不断被加载,最终导致Perm区被占满。

Jdk1.6及之前: 有永久代, 常量池1.6在方法区

Jdk1.7: 有永久代,但已经逐步“去永久代”,常量池1.7在堆

Jdk1.8及之后: 无永久代,常量池1.8在元空间

其中1.1.2节参考博文原文链接:https://blog.csdn.net/codeyanbao/article/details/82875064

java虚拟机——轻松搞懂jvm的更多相关文章

  1. 举一个有趣的例子,让你轻松搞懂JVM内存管理

    目录 前言 例子 源码 输出 图解 深入分析 学以致用 写在最后 前言 在JAVA虚拟机内存管理中,堆.栈.方法区.常量池等概念经常被提到,对理论知识的理解也常常停留在字面意思上,比如说堆内存中存放对 ...

  2. 一夜搞懂 | JVM 字节码执行引擎

    前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习字节码执行引擎? 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一 ...

  3. java虚拟机学习-慢慢琢磨JVM(2-1)ClassLoader的工作机制

    ClassLoader的工作机制 java应用环境中不同的class分别由不同的ClassLoader负责加载. 一个jvm中默认的classloader有Bootstrap ClassLoader. ...

  4. Java虚拟机系列一:一文搞懂 JVM 架构和运行时数据区

    前言 之前写博客一直比较随性,主题也很随意,就是想到什么写什么,对什么感兴趣就写什么.虽然写起来无拘无束,自在随意,但也带来了一些问题,每次写完一篇后就要去纠结下一篇到底写什么,看来选择太多也不是好事 ...

  5. 轻松搞懂Java中的自旋锁

    前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...

  6. 一夜搞懂 | JVM 线程安全与锁优化

    前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习内存模型与线程? 之前我们学习了内存模型和线程,了解了 JMM 和线程,初步 ...

  7. Java虚拟机3:常用JVM命令参数

    之后写的东西就会用到虚拟机参数了,现在这里汇个总自己平时用到的.看到的一些虚拟机参数.现在看不懂没关系,反正之后都会用到的: (1)-Xms20M 表示设置堆容量的最小值为20M,必须以M为单位 (2 ...

  8. java虚拟机学习-慢慢琢磨JVM(2)

    1 JVM简介 JVM是我们Javaer的最基本功底了,刚开始学Java的时候,一般都是从“Hello World”开始的,然后会写个复杂点class,然后再找一些开源框架,比如Spring,Hibe ...

  9. java虚拟机学习-深入理解JVM(1)

    1   Java技术与Java虚拟机 说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言.Java类文件格式.Java虚拟机和Java应 ...

随机推荐

  1. 解决Windows2019登录黑屏问题

    打开服务 停止App Readiness服务并禁用即可恢复正常

  2. win7激活不支持的启动引导分区完美解决方法

    前言: 激活win7显示不支持的启动引导分区怎么办?有用户使用暴风激活工具给win7 64位系统激活时,弹出Error提示框"不支持的启动引导分区". 这是因为传统的win7激活工 ...

  3. 微服务 - 服务消费(七)Feign

    介绍 Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端.它使得编写Web服务客户端变得更加简单.我们只需要通过创建接口并用注解来配置它既可完成对Web ...

  4. java IO 模型--快速分清 同步|阻塞

    IO 介绍 IO 模型 IO请求 分为两个阶段:等待资源 和 使用资源: IO请求:一般需要请求特殊资源(如 磁盘.RAM 或文件),当资源被上一个使用者使用没有释放的时候, IO请求会被阻塞,直到资 ...

  5. checkBox判断是否选中的方法

    这里可以分为两种情况:JQuery对象和DOM对象: 通常我们用JQuery判断元素的属性的时候喜欢用 attr("attrName"); 但是尝试过的同学可能都知道,这种方法判断 ...

  6. java中使用IO流将以文件中的内容去取到指定的文件中

    public class Demo12 { public static void main(String[] args) throws IOException { File file=new File ...

  7. 高性能、低成本的高防 IP 产品能现实吗?

    DDoS 攻击是网络攻击最常用的方式之一,也是企业发展道路上的阻碍.作为业务发展的巨大隐形"地雷",企业想要自建 DDoS 防御的技术门槛很高,且建设周期不可控.这给予了安全厂商海 ...

  8. 96. Unique Binary Search Trees1和2

    /* 这道题的关键是:动态表尽量的选取,知道二叉搜索树中左子树的点都比根节点小,右子树的点都比根节点大 所以当i为根节点,左子树有i-1个点,右子树有n-i个点,左右子树就可以开始递归构建,过程和一开 ...

  9. 配置 nginx 访问资源目录,nginx配置 root 与 alias 的区别

    比如说想要把 /home/source 目录作为资源目录,那么需要如下配置: location /source/ { #识别url路径后,nginx会到/home/文件路径下,去匹配/source r ...

  10. spring之ApplicationEvent 事件驱动

    什么是ApplicationContext? 它是Spring的核心,Context我们通常解释为上下文环境,但是理解成容器会更好些. ApplicationContext则是应用的容器. Sprin ...