Java-Jvm-01-简介

1. Jvm体系结构

在这里插入图片描述
Java1.8 前后关于永久代移除,替换成元空间的截图
在这里插入图片描述
在这里插入图片描述

2. Jvm的GC作用域

在这里插入图片描述

相关介绍:

  • Native
    声明是本地方法库,实现无 是一个关键字
  • PC寄存器
    记录了方法之间的调用和执行情况,类似排班值班表,用来存储指向下一条指令的地址,也即将来要执行的指令命令代码,它是当线程所执行的字节码的符号指示器。
  • 方法区
    它存储了每一个类的结构信息,方法区是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代(PerGen space)和元空间(Metaspace);
  • Stack栈
    也叫内存,主管java程序的运行,是在线程创建时创建,它的生命周期是跟随线程的生命周期,线程结束内存也就释放了,对于栈来说不存在垃圾回收问题。只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。8种基本类型的变量+对象的引用变量+实列方法都是在函数的栈内存中分配。
  • 栈帧
    (方法在栈内叫栈帧 )中主要保存3类数据;
  • 本地变量
    输入参数和输出参数以及方法内的变量
  • 栈操作
    记录出栈和入栈的操作
  • 栈帧数据
    包括类文件,方法等
  • 堆 Heap
    Java7之前,叫永久代(Permanent Space ),java8 叫元空间(Metaspace)

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

堆内存:

  • 逻辑上分为部分: 新生+养老+永久
  • 物理上:新生+养老
    新生区 Eden Space
    幸存0区 Survivor 0 space
    幸存1区 Survivor 1 space
    养老区 Tenure Generation Space

为什么要将java7的永久代更改为元空间?

  • Java7及以前版本的Hotspot中方法区位于永久代中。同时,永久代和堆是相互隔离的,但它们使用的物理内存是连续的。
    永久代的垃圾收集是和老年代捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的垃圾收集。

  • 在Java8中,元空间(Metaspace)登上舞台,方法区存在于元空间(Metaspace)。同时,元空间不再与堆连续,而且是存在于本地内存(Native memory)。本地内存(Native memory,也称为C-Heap,是供JVM自身进程使用的。当Java Heap空间不足时会触发GC,但Native memory空间不够却不会触发GC。
    元空间存在于本地内存,意味着只要本地内存足够,它不会出现像永久代中“java.lang.OutOfMemoryError: PermGen space”这种错误

  • 个人观点:
    表面上看是为了避免OOM异常。因为通常使用PermSize和MaxPermSize设置永久代的大小就决定了永久代的上限,但是不是总能知道应该设置为多大合适, 如果使用默认值很容易遇到OOM错误。
    当使用元空间时,可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。

GC理解

  • 简单版
    主要分为下面几个步骤:

    伊甸园区  Eden Space
            |
    幸运者0区  Survivor 0 space  简称  S0 也叫 from区
            |  (存在交换)
    幸运者1区  Survivor 1 space   简称 S1 也叫 To区
            |
     养老区 Tenure Generation Space
    

假设现在new100个士兵去打仗,死了98个,剩下的2个会进入幸运0区,再new100个去打仗,加上之前幸运0区的一共102个,这次死了99个,2个老兵没死外加一个新兵,
以此循环几次,幸运0区满了,移到幸运1区,1区满了移到养老区

Eden满了,开启GC GC也叫YGC 即 Young GC,Eden基本清空, 15次之后活下来的进入养老区,Old养老区满了,开启FGC 即 Full GC,养老区发现满了,也没办法腾出位置,才会报 outofmemoryerror 内存溢出异常,简称 OOM,新生区是类的诞生,成长,销毁的区域,一个类在这里产生,应用,最后被垃圾回收器回收,结束生命。
新生区又分为两个部分,伊甸园区(Eden space)和幸运区(Servivor space),所的类都是在伊甸园区被new出来。幸运区两个:幸运0区 Survivor 0 space 和幸运1区 Survivor 1 space
当伊甸园区的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁,然后对伊甸园区剩下对象移到幸运0区,若幸运0区满了,再对0区进行垃圾回收,剩下的移到1区。如果1区也满了,就移到养老区,如果养老区也满了,那么会产生MajorGc(Full Gc),进行养老区的内存清理。若养老区清理之后仍然无法完成对象的保存,则会产生OOM异常(outofmemoryerror)

如果出现了OOM异常,说明虚拟机的堆内存空间不够,原因两个:

  1. java虚拟机的堆内存设置不够,可以通过参数 -Xms -Xmx来调整
  2. 代码中出现大量大对象,并且长时间不能被垃圾回收器收集(存在被引用)
  • 详细版GC
    java堆从GC的角度还分为 新生代(Eden区,From Survivor区,To Survivor区)和老年代。
    minor英文翻译: 未成年的,小的 major:成年的
    MinorGc的过程(复制-清空-互换)
    1: Eden,SurvivorFrom复制到SurvivorTo,年龄加1
    首先,当Eden区满时会触发第一次Gc,把还活着的对象拷贝到SurvivorFrom区,当Eden区再次触发Gc时会扫描Eden区和From区的区域,对这两个区域进行垃圾回收,经过这次回收还活着的对象,则直接复制到To区域(如果对象的年龄已经达到了老年的标准,则赋值给老年代区),同时把这些对象年龄+1;
    2: 清空 Eden,SurvivorFrom
    然后清空Eden和SurvivorFrom中的对象,也即复制之后交换,
    【谁空谁是To】 (SurvivorFrom和SurvivorTo两个区域是动态变化的)
    3: SurvivorTo和SurvivorFrom互换
    最后,SurvivorTo和SurvivorFrom互换,原SurvivorTo成为下一次Gc时的SurvivorFrom区。部分对象会在From区和To区复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认为15)最终如果还是活着则进入老年代(Tenuring: 英文翻译为 任期;占 Threshold :阈值)

3. Jvm的类加载

首先,我们编写的 .java文件是存储在磁盘当中,但是.java文件是不可被机器识别,需要先编译,于是会通过java compile编译器将.java文件编译成.class文件;
负责加载class文件,class文件在文件开头有特定的标识,cafe babe,将class文件字节码内容加载进内存中运行时数据区的方法区的运行时数据结构并且classLoad只负责加载,至于能否运行则由Execution Engine决定

通过类加载器加载进内存:

其包括: 加载(loading) ——> 链接(验证,准备,解析) ——>初始化——>制定一个类对象——>找到main方法 -》进入方法区

  • 加载:在方法区 static修饰的方法和变量是成为类对象的方法和变量,而未被static修饰的方法和变量是说明实列内容的,当在该类当中定义一些局部变量时,会在栈的栈帧当中开辟一个空间存放,当变量new一个对象时,会在堆内存当中开辟一个空间,由该栈帧中的对象变量指向堆内存空间的对象

  • 验证过程: 验证编译后的二进制数据不会危害虚拟机

  • 准备过程: 将static修饰的静态变量初始化并且分配内存空间,注意,比如 static int 这里只会赋值为0

  • 解析:将符号引用转换成直接引用

  • 初始化: 给static修饰的赋值

注意: 被final修饰的变量在编译期,也就是.java转.class就被分配了内存

这里要注意两个概念: 类对象和对象

  • 类对象是类加载器加载到内存时制定的对象,存在于方法区,只包含静态方法,可以理解为说明书
  • 对象是在栈帧当中new出来的指向堆当中的对象,根据说明书new出来的实列

类加载器分为java虚拟机自带的类加载器,另外一种是用户自定义的类加载器,是java.lang.ClassLoader的子类实列:

根类加载器(Bootstrap) 用来加载jre/lib/rt.jar ,没父类,打印该加载器为null,由C语言编写;
拓展类加载器(Extension) 由java语言编写,父加载器是根类加载器,加载jre/lib/ext类库,打印出来为Ext开头
系统类加载器/应用加载器(System) 纯java类,加载程序中所的类。
父类加载器是拓展类加载器 打印出来是APPClass开头

注意:java虚拟机对class文件采用的是需加载的方法,也就是说当需要使用该类时才会将class文件加载到内存生产class对象
而加载class文件时,java虚拟机采用的是双亲委派模式,即把加载类的请求交给父加载器处理,一直到根类加载器,如果父类加载器加载不到时才会自己加载

使用双亲委派的好处:

  • 1.可以避免类的重复加载,当父类加载器已经加载该类时,就没必要子ClassLoader再加载一次,这样保证内存中只有一份类
  • 2,可以防止java核心api库不会被随意串改,假设人定义一个名字叫java.lang.object的类,当通过双亲委派模式传递到启动类加载器时,发现已经被加载,就不会重新加载传递过来的类,直接返回已经加载过的类,起到安全性保障。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alan0517

感谢您的鼓励与支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值