当前位置:首页 > Java技术 > JVM内存模型简述

JVM内存模型简述

2022年11月09日 15:32:58Java技术9

JVM内存模型包括:类加载器、执行引擎、本地方法库、运行时数据区

JVM内存模型简述 _ JavaClub全栈架构师技术笔记

 

 1.类加载器

  JVM中类加载器会把 Java 代码转换成字节码,主要使用双亲委派机制实现类的加载,加载机制为:当前程序类-->扩展程序类-->根加载器(rz.jar)

类加载器分类:

  • 启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
  • 扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;
  • 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

双亲委派模型:

  如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

2.执行引擎

  class文件经过加载后会在方法区形成一个该类的一些数据结构(或者说是Java虚拟机规定的格式),然后执行引擎会在方法区中找到main方法code属性中的指令,并且去执行它。执行引擎会维护一个虚拟机栈,虚拟机栈中有栈帧,栈帧中维护者操作数栈、局部变量表和指向该栈帧所属的方法在运行时常量池中的引用

3.本地方法库

  1. 由于java早期出现是为了优化c++的一些冗余弊端,所以为了和c或者c++能够相通,java中使用了native关键字来定义一些不是使用java实现的方法,也就是现在的本地方法库。 
  2. native:凡是带有native关键字的,说明java的作用范围达不到了,会去调用底层C语言的库!会进入本地方法栈,调用本地方法接口java nation implment(JNI)
  3. JNI的作用:扩展java的使用,融合不同的编程语言为java所用,最初:C、C++

4.运行时数据区(重点)

首先来看一个简单程序代码:

public class JVMModel {
    public void math(){
        int a = 1;
        int b =2;
        int c = a+b;
    }
    public static void main(String[] args){
        JVMModel jvmModel = new JVMModel();
        jvmModel.math();
    }
}

运行时数据区执行内存模型为下图所示:

JVM内存模型简述 _ JavaClub全栈架构师技术笔记

 

 

 下面结合上边程序和模型图进行JVM内存模型分析

(1)程序计数器

  程序计数器是一块较小的内存空间,可以看作是当前线程执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

    java虚拟机的多线程是通过线程流切换并分配CPU的时间片的方式实现的,因此在任何时刻一个处理器(如果是多核处理器,则只有一个核)都只会处理一个线程,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各线程之间计数器互相不影响,独立存储,因此这类内存区域为“线程私有”的内存。

   由此可总结出程序计数器的作用;

  (1)字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

  (2)在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次执行位置。

*由于程序计数器是用来记录执行链地址的,所有生命周期随着线程是一致的,也就不存在OOM(OutOfMemoryError内存溢出)的区域了。

(2)栈(线程栈)

  java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是java方法执行的内存模型,java虚拟机栈是由一个个栈帧组成,线程在执行一个方法时,便会向栈中放入一个栈帧,每个栈帧都拥有局部变量表、操作数栈、动态链接、方法出口信息。局部变量表主要存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)和对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其它与此对象相关的位置)。

  Java虚拟机会出现以下两种异常:

  (1)StackOverFlowError:若Java虚拟机的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度,就会抛出StackOverFlowError;

  (2)OutOfMemoryError:若Java虚拟机栈的内存大小允许动态扩展,且当线程请求栈的内存用完了,无法再动态扩展了,此时就会抛出OutOfMemoryError;

*Java虚拟机栈也是线程私有的,其生命周期是随着线程的创建而创建,随着线程死亡而死亡。

(3)本地方法栈

  本地方法栈的起源是由于java出现早期,java很多方法都是使用C编写的(Java是基于C发展的,过渡期),所以在JVM内存中针对这部分方法开辟了本地方法栈。

  和虚拟机所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。在HotSpot虚拟机中和Java虚拟机栈合二为一。

  本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该方法的局部变量表、操作数栈、动态链接、出口信息。

*本地方法栈同样也会出现StackOverFlowError和OutOfMemoryError异常

(4)堆

  堆是java虚拟机中所管理的内存中最大的一块,java堆是所有线程共享的一块区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。几乎所有的对象实例以及数组都在这里分配内存(目前由于编译器的优化,对象在堆上分配已经没有那么绝对了。

  Java 堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap)。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代:其中新生代又分为:Eden空间、From Survivor、To Survivor空间。进一步划分的目的是更好地回收内存,或者更快地分配内存。“分代回收”是基于这样一个事实:对象的生命周期不同,所以针对不同生命周期的对象可以采取不同的回收方式,以便提高回收效率。从内存分配的角度来看,线程共享的java堆中可能会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。

   JVM内存模型简述 _ JavaClub全栈架构师技术笔记

  如图所示,JVM内存主要由新生代、老年代、永久代构成。

  ① 新生代(Young Generation):大多数对象在新生代中被创建,其中很多对象的生命周期很短。每次新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。

  新生代内又分三个区:一个Eden区,两个Survivor区(一般而言),大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到两个Survivor区(中的一个)。当这个Survivor区满时,此区的存活且不满足“晋升”条件的对象将被复制到另外一个Survivor区。对象每经历一次Minor GC,年龄加1,达到“晋升年龄阈值”后,被放到老年代,这个过程也称为“晋升”。显然,“晋升年龄阈值”的大小直接影响着对象在新生代中的停留时间,在Serial和ParNew GC两种回收器中,“晋升年龄阈值”通过参数MaxTenuringThreshold设定,默认值为15。

  ② 老年代(Old Generation):在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代,该区域中对象存活率高。老年代的垃圾回收(又称Major GC)通常使用“标记-清理”或“标记-整理”算法。整堆包括新生代和老年代的垃圾回收称为Full GC(HotSpot VM里,除了CMS之外,其它能收集老年代的GC都会同时收集整个GC堆,包括新生代)。

  ③ 永久代(Perm Generation):主要存放元数据,例如Class、Method的元信息,与垃圾回收要回收的Java对象关系不大。相对于新生代和年老代来说,该区域的划分对垃圾回收影响比较小。

*在 JDK 1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域(永久代使用的是JVM的堆内存空间,而元空间使用的是物理内存,直接受到本机的物理内存限制)。

(5) 方法区

  方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。

  HotSpot 虚拟机中方法区也常被称为 “永久代”,本质上两者并不等价。仅仅是因为 HotSpot 虚拟机设计团队用永久代来实现方法区而已,这样 HotSpot 虚拟机的垃圾收集器就可以像管理 Java 堆一样管理这部分内存了。但是这并不是一个好主意,因为这样更容易遇到内存溢出问题。

*相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。

(6) 运行时常量池

  运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池信息(用于存放编译期生成的各种字面量和符号引用)

  既然运行时常量池时方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

  JDK1.7及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。

   JVM内存模型简述 _ JavaClub全栈架构师技术笔记

(7)直接内存

  直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致OutOfMemoryError异常出现。

  JDK1.4中新加入的 NIO(New Input/Output) 类,引入了一种基于通道(Channel) 与缓存区(Buffer) 的 I/O 方式,它可以直接使用Native函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。

  本机直接内存的分配不会收到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。

作者:小小吸血鬼
来源链接:https://www.cnblogs.com/smallVampire/p/12572925.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/68909.html

分享给朋友:

“JVM内存模型简述” 的相关文章

Java虚拟机1:什么是Java

Java虚拟机1:什么是Java

前言 让我们来看一下Java的广告词,来自http://www.java.com/zh_CN/about/: 97%的企业桌面运行Java 美国有89%的桌面(或计算机)运行Java 全球有900万Java开发人员 开发人员的头号选择...

性能优化|一张图带你了解JVM是如何进行内存分配

性能优化|一张图带你了解JVM是如何进行内存分配

对象优先在新生代分配 如果对象能够在eden区分配,那么就直接在eden区分配 如果eden区域大小不够新对象存储的话,则会触发一次minor gc, 如果minor gc后发现对象也无法在s区分配,则会直接在老年代中分配...

IDEA启动失败,报Failed to create JVM错误的解决办法

IDEA启动失败,报Failed to create JVM错误的解决办法

IDEA启动失败,报Failed to create JVM错误的解决办法 从网上下载的破解版IDEA,照网上的pojie方法进行破解时,很多人是用Windows自带的记事本添加并保存的。但是用记事本保存的文件是带有BOM的,从而导致启动IDEA报...

[JVM教程与调优] 为什么要学习JVM虚拟机?

[JVM教程与调优] 为什么要学习JVM虚拟机?

JVM在我们开发阶段不会用到,但是到了生产环境中,那么就会变得非常重要了。 为什么这么说呢? 一方面,因为我们的生产环境是比较复杂的。各种可能的问题都会出现,比如说:硬盘坏了、网络坏了、CPU利用率高了等问题层次不穷。 另外一方面,在我们生产环境出现问题,还不好进行定位。因为没...

JVM学习1--数字存储,内存模型,指令重排

JVM学习1--数字存储,内存模型,指令重排

一、数字在计算机中的存储   整数:以补码形式存储。     补码:正数的补码是自身,负数的补码是取反码加1(取反码时符号位还是1)   浮点型:以float类型表示            注意一下,这八位指数实际上是(127...

【Java】JVM内存模型解析

【Java】JVM内存模型解析

   JVM内存模型主要分为五大区域:栈、堆、本地方法栈、程序计数器、方法区。   本地方法栈: 跟虚拟机栈非常相似,也是线程私有的,不过虚拟机栈是针对Java方法,而本地方法栈是针对native方法,也就是底层方...

JVM 算法简介

JVM 算法简介

JVM学习目录 1.JVM 概念简介 2.JVM 运行时内存 3.JVM算法简介 4.JVM 垃圾收集器 5.JVM 调优实战 通过以上的文章我们对JVM有了初步的认识,在前几篇文章中也说过一些算法知识点,今天这篇文章我们进行算法的分享 一、...

jvm-java 内存模型  以及各个分区具体内容

jvm-java 内存模型 以及各个分区具体内容

java内存模型,这里其实是指 jvm运行时内存模型 1 每一个应用程序,都有一个JVM 而不是 多个应用程序,共享一个jvm 2 java源文件,首先通过编译器,把java语法的代码,编译成 jvm语法的字节码文件 这个过程,是不涉及到...

jvm中关于slot的理解

jvm中关于slot的理解

jvm中关于slot的理解 参数值的存放总是在局部变量数组的index0开始,到数组长度-1的索引结束。 局部变量表,最基本的存储单元是slot(变量槽) 局部变量表中存放编译期可知的各种基本数据类型(8种),引...

JVM-五大区

JVM-五大区

Java程序执行流程: 谈一谈Java内存区域的划分实际上是指JVM内存区域的划分,首先Java先介绍一下Java程序具体执行的过程 如上图所示,首先Java源代码们(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。