JVM

此篇文章是LZ看视频总结的一点关于JVM的知识点,有兴趣的小伙伴可以了解一下:

https://www.bilibili.com/video/BV1vE411D7KE?p=14

简介

首先熟悉下JVM体系结构
在这里插入图片描述JVM内存模型主要就是5个部分:方法区,堆,堆栈,本地方法栈,PC计数器

类加载过程

当程序第一次使用一个类的时候,类里信息还没初始化,需要通过类加载器进行加载,JVM会通过加载、连接、初始化3个步骤来对该类进行初始化

  1. 加载: 加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。类的加载由类加载器完成

  2. 链接:当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类链接又可分为如下个阶段。
    验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
    准备:类准备阶段负责为类的静态变量分配内存,并设置默认初始值。
    解析:将类的二进制数据中的符号引用替换成直接引用

  3. 初始化:初始化是为类的静态变量赋予正确的初始值,比如private static int a=5;在准备阶段只是将a设置为默认初始值,而在初始化的时候才真正意义上将a赋值为5;

类装载器

类加载器简而言之就是用来加载类的东西

在这里插入图片描述由图可见,class字节码先通过ClassLoader加载并初始化

当JVM启动的时候,Java开始使用如下三种类型的类加载器:
JVM自带的加载器:

  • 启动类加载器(Bootstrap classLoader):又称为引导类加载器,由C++编写。由于启动类加载器涉及到虚拟机本地实现细节,比如Object.class,String.class这些初始的类就在这里进行加载。开发者无法直接获取到启动类加载器的引用。
  • 扩展类加载器(Extension classLoader):开发者可以直接使用标准扩展类加载器,具体可由扩展类加载器加载到的路径可通过System.getProperty(“java.ext.dirs”)查看。
  • 系统类加载器(System classLoader):开发者可以直接使用系统类加载器,java默认的加载器

自定义加载器:

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

可以看出ClassLoader.getSystemClassLoader得到的类加载器是AppClassLoader也就是System classLoader,是java默认的类加载器,而它的父类是Extension classLoader,而Extension classLoader的父类则为null,这也验证了之前说的开发者无法直接获取启动类加载器
在这里插入图片描述
那到底类加载器怎么加载?下面旧涉及到双亲委派机制

双亲委派机制

下图是各加载类的父子关系,接下来说双亲委派机制
在这里插入图片描述双亲委派过程:当一个类收到类加载请求,他首先不会尝试自己去加载这个类,而是把这个类的请求转发给父类去完成,每个类加载器都是这样,先找父类,如果没有父类就停止,也就是说每个类加载器最终都要到启动类加载器中,只有父类无法完成子类的请求(在它的加载路劲下没有找到所需加载的Class),这时候请求才会反馈到子类,由子类自己加载。

采用双亲委派机制的好处就是防止有些人恶意篡改底层类,比如加载rt.jar(这个jar中都是底层Class)中的类java.lang.Object,不管是那个加载器加载的这个类,最终都会委托给最高层的启动类加载器进行加载,这样防止有人自己定义一个java.lang.Object,保证了使用Object类的时候都是使用的同一个Object类
在这里插入图片描述

方法区

线程共享,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,简单来说存储的是类的结构信息

字符串常量池放在方法区?

字符串常量池到底存在于内存空间的哪里?
JDK7.0 之前 字符串常量池在方法区
JDK7.0 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆中开辟了一块区域存放运行时常量池。
JDK1.8开始,取消了Java方法区,JVM又声明了元空间,取而代之的是位于直接内存的元空间

线程共享,主要是存放对象实例和数组。主要是GC管理
而对象分为对象头、实例数据和对齐填充。

堆中的分区

了解堆概念之后,那么大家回想堆中是怎么管理对象,GC是怎么回收的?
堆中大致分为三个部分

  • 新生区:新生区分为伊甸区(Eden space),幸存0区(Survivor 0 space),幸存1区(Survivor 1 space)
    其中幸存0区和幸存1区又叫from区和to区,记住一个概念“谁空谁是TO”这后面会讲
  • 养老区
  • 永久存储区

逻辑上:上面说的新生区,养老区,永久区(元数据)是逻辑上存在
物理上:而在物理上堆只分为新生区,养老区

首先看下下图,堆大体上分为新生区和老年区,新生区占堆内存的1/3,老年区占2/3
而新生区分为Eden,From,To三区,各占Youny区的8/10,1/10,1/10。

在这里插入图片描述
接下来说对象存储位置和GC如何清理

对象在堆中的存储流程

  1. 首先对象创建号之后,会先存储在新生区中的Eden区,from和to区先不存,等Eden区存满了之后会触发第一次GC,开始清理一些无用的对象,而活着的对象age+1,并拷贝到From区,这时候From区是幸存0区。
  2. 第一次结束之后,又有对象创建,存储在Eden区,然后Eden又满了,这时候又会触发GC然后,会清理Eden区和From区,将幸存的对象age+1(如果对象的age达到了15则会存放至Old养老区),并拷贝到To区,之前说过一个概念谁空谁是To,这时候To区就变成了From区,而上一次的From区则成了To区。
  3. 然后依次执行上面2个步骤,这时候肯定一些对象age达到15会进入老年区,当老年区满了之后产生FullGC进行老年区内存清理,若执行FullGC后还是不能进行对象的保存,则会报OOM(Out Of Memory)
    4.OOM出现的情况:1.JVM对内存设置不够 2.创建大量对象,长时间不能被GC清理(存在被引用)

永久存储区

永久区JDK7的时候叫永久代,JDK8之后叫元空间
简单理解永久区就是一个常驻内存区域,用于存放JDK自身携带的class,interface的元数据,也可以说存放jar
也就是说它存储的是运行环境必要的类信息,这里是不会被GC回收,只有关闭JVM才会释放此区域的内存

GC常用算法

  1. 引用计数法:没引用的对象计数减1,为0的时候直接回收。
    缺点:1.需要维护,本身通过计数有消耗。2.较难处理循环
    这个算法只做了解,基本现在不会用这种算法

  2. 复制算法:主要用于新生代(新生区),from-to交换,之前有讲过。
    优点:没有内存碎片。
    缺点:耗内存。

  3. 标记清除:主要用于老年代(老年区),在同一个区域,通过先标记需要清理的对象,然后将其清除,。
    优点:不需要额外空间。
    缺点:有内存碎片,两次扫描,耗时。

  4. 标记压缩:基于标记清除算法,先标记,然后整理压缩,使之连续不存在内存碎片,也是同一个区域
    优点:无内存碎片,不需要额外空间
    缺点:耗时

如何使用这些算法?
不同的代用不同的算法。

java本地方法栈

线程私有,区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务(有声明,无实现,主要是支持C/C++)。也会有 StackOverflowError 和 OutOfMemoryError 异常。

java虚拟机栈

线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。

特点
1.栈管运行,堆管存储
2.栈也叫栈内存,主管java程序运行,它依附线程,随线程创建而创建消失而消失
3.对于栈不存在垃圾回收,只要线程结束就over
4.存储8种基本类型的变量+对象的引用变量+实例方法

什么叫栈帧?
栈帧=java方法放入JVM的栈
栈帧中主要保存3类数据

  1. 本地变量:输入/输出参数及方法内的变量
  2. 栈类操作:记录出/入栈的操作
  3. 栈帧数据:包括类文件,方法等

程序计数器

线程私有。字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成
JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器),占用内存很小,它是当前线程所执行的字节码的行号指示器

总结

我们了解JVM后会明白,大致至流程是,首先一个.java文件会生成一个.class文件,然后通过类加载加载.class文件(字节码文件),然后各类信息各自存储在JVM内存模型中,比如,对象存储在堆中,方法存在栈中。因此JVM内存模各自的分工就清楚了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值