浅谈 java 虚拟机 JVM

 前言

小亭子正在努力的学习编程,接下来将开启JavaEE的学习~~

分享的文章都是学习的笔记和感悟,如有不妥之处希望大佬们批评指正~~

同时如果本文对你有帮助的话,烦请点赞关注支持一波, 感激不尽~~

目录

 前言

JVM中的内存划分

JVM的类加载机制

1.加载 :

2.验证:

3.准备:

4.解析:

5.初始化

双亲委派模型

JVM中的垃圾回收机制 ( GC)

GC主要分为两个阶段:

找谁是垃圾

1.引用计数:

2.可达性分析

释放内存

标记清除

复制算法

标记整理

垃圾收集器

⾯试题 :


JVM中的内存划分

java其实就是一个java进程,会从操作系统中申请一大块内存区域给java代码使用,并进一步划分给出不同的用途。 

JVM 运行时数据区域也叫内存布局,但需要注意的是它和 Java 内存模型((Java MemoryModel,简称JMM)完全不同,属于完全不同的两个概念,它由以下 5 大部分组成:

  •  程序计数器: 用途是记录当前程序执行到那个指令了,里面是简单的long 类型的变量存了一个内存地址,这个内存地址就是下一个要执行的字节码所在的地址。
  • 本地方法栈:是给JVM内部的本地方法使用的【JVM内部是c++实现的】
  • 虚拟机栈:是给java代码使用的
  • 堆:程序中创建的所有对象都在保存在堆中
  • 方法区/元数据区:用来存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据的。

注意:堆和元数据区【共享的】,在一个JVM中只有一个,栈和程序计数器则是有多份,每个线程都有一份【线程私有的】。

其中,最核心的区域:

1.堆  :new 出来的对象,【成员变量】

2.栈 : 维护方法之间的调用关系 【局部变量】

3. 方法区/元数据区 /元空间 :放的是类加载之后的类对象 【静态变量】

浅谈 java 虚拟机 JVM_第1张图片


JVM的类加载机制

类加载的过程:把 .class 文件加载到内存,得到类对象这样的过程浅谈 java 虚拟机 JVM_第2张图片

分为五步:加载,验证,准备,解析,初始化

1.加载 :

找到.class文件,并读文件内容

2.验证:

验证选项:
文件格式验证
字节码验证
符号引用验证...

.class文件有明确的数据格式(二进制的)

浅谈 java 虚拟机 JVM_第3张图片

3.准备:

给类对象分配内存空间(未初始化的空间,数据全是0的,类对象中的静态变量啥的全是0,相当于租了个毛坯房)

4.解析:

针对字符串常量进行初始化【字符串常量池中的符号引用替换为直接引用的过程】

相当于之前是只知道彼此之间的相对位置,这时候字符串常量就是符号引用,真正加载到内存中就会把字符串常量填充到内存的特定位置上,字符串之间的相对位置还是一样,但是这些字符串有了自己真正的内存地址。此时的字符串就是直接引用(java中的普通引用)

【打个比方:开学排座位的时候,学校通知按照学号排,你只知道你前面一个位置是小明的,但是你不知道你具体坐在第几排,排好座位以后你知道了自己在第三排,小明在第二排,你们知道了自己的具体位置,并且你们之间的相对位置没变,小明还是在你前面一个。】

5.初始化

针对类对象进行初始化(初始化静态成员,执行静态代码块,类要是有父类还需要加载父类)

注意:类加载这个动作不是JVM一启动就把所有.class文件都加载的,整体是一个“懒加载”的策略(懒汉模式),非必要不加载。

所谓的“必要”情况:

  1. 创建了这个类的实例
  2. 使用了这个类的静态方法/属性
  3. 使用了子类,会触发父类的加载

双亲委派模型

【说在前面:双亲其实只有一个准确的说是父亲,(都是翻译的锅)】

JVM中加载类,需要用到一组特殊的模块,类加载器,JVM内置了三个类加载器(程序员也可以定义自己的类加载器)

  1. BootStrap  ClassLoader:启动类加载器:加载 JDK 中 lib ⽬录中 Java 的核⼼类库,即$JAVA_HOME/lib⽬录
  2. Exttension ClassLoader :扩展类加载器。加载 lib/ext ⽬录下的类,非标准的但是是Sun/Oreacle 扩展的库的类库
  3.  Application ClassLoader:应⽤程序类加载器。负责加载项目中自己写的类,以及第三方库中的类。

具体加载一个类的过程:
浅谈 java 虚拟机 JVM_第4张图片注意:双亲委派模型是可以打破的,比如tomcat就没有遵守


JVM中的垃圾回收机制 ( GC)

垃圾回收机制主要是帮助程序员自动释放内存的

【这里说的是基本思想,JVM具体是怎么实现的,这个问题很复杂,就不讨论了】

程序计数器,就是单纯存地址的整数,跟随线程一起销毁

栈,方法调用完毕,方法的局部变量也随着线程一起销毁

元数据区,存的是类对象,很少会“卸载”

所以,堆就是GC的主要目标

GC是以对象为单位进行释放的,【说是释放内存,其实就是释放对象】

GC主要分为两个阶段:

找谁是垃圾,释放内存

找谁是垃圾

一个对象,如果后序不在用了,就可以认为是 垃圾

java中使用一个对象,只能通过引用,所以java单纯地通过引用有没有指向来判定是不是垃圾

通常有两种方法:引用计数,可达性分析

1.引用计数:

给对象安排一个额外的空间,保存一个整数,表示该对象有指向【java没有使用这个方法,Python,php用了】

浅谈 java 虚拟机 JVM_第5张图片

2.可达性分析

把对象之间的引用关系理解成一个树形结构,从一些特殊的起点出发。进行遍历,只要遍历能访问到的对象就是“可达”的,再把“不可达”的当做垃圾即可。 

浅谈 java 虚拟机 JVM_第6张图片

 可达性分析关键要点进行上述遍历需要有  “起点”

  1. 栈上的局部变量(每个栈的每个局部变量都是起点。)
  2. 常量池中引用的对象
  3. 方法区中静态成员引用的对象。

可达性分析总的来说就是从所有的gc roots的起点出发,看看该对象里又通过引用能访问哪些对象,顺藤摸瓜的,把所有可以访问的对象都给变了一遍。便利的同时把对象标记成可达,剩下的自然就是不可达。

可达性分析克服了引用技术的两个缺点。但是也有自己的问题。

  1. 因为要遍历所有对象,需要消耗更多的时间,因此某个对象成了垃圾,也不能第一时间发现。
  2. 在进行可达性分析的时候要顺藤摸瓜,一旦这个过程中当前代码中引对象的引用关系发生了变化,就会出现问题。【STW问题(让其他业务线暂停),现在已经能很好的应对了】

释放内存

有三种策略:标记清除,复制算法,标记整理

标记清除

浅谈 java 虚拟机 JVM_第7张图片

缺点:内存碎片化

复制算法

浅谈 java 虚拟机 JVM_第8张图片

缺点:

内存空间利用率低,复制成本高

标记整理

浅谈 java 虚拟机 JVM_第9张图片


上述三种策略都有缺点,所以,实际上 JVM 的实现思路,是结合了上述几种思想方法,针对不同情况采取不同策略,产生了 :分代回收思想

浅谈 java 虚拟机 JVM_第10张图片

垃圾收集器

如果说上⾯我们讲的收集算法是内存回收的⽅法论,那么垃圾收集器就是内存回收的具体实现。

【本文就不介绍了。】


⾯试题 :

请问了解Minor GC和Full GC么,这两种GC有什么不⼀样吗?

1. Minor GC⼜称为新⽣代GC : 指的是发⽣在新⽣代的垃圾收集。因为Java对象⼤多都具备朝⽣夕灭的特性,因此MinorGC(采⽤复制算法)⾮常频繁,⼀般回收速度也⽐较快。

2. Full GC ⼜称为 ⽼年代GC或者Major GC : 指发⽣在⽼年代的垃圾收集。出现了Major GC,经常会伴随⾄少⼀次的MinorGC(并⾮绝对,在Parallel Scavenge收集器中就有直接进⾏Full GC的策略选择过程)。Major GC的速度⼀般会⽐MinorGC慢10倍以上


以上就是本文分享的主要内容,对你有帮助的话,可以点个赞哦~~

你可能感兴趣的:(java,EE,java,jvm,开发语言)