前言
小亭子正在努力的学习编程,接下来将开启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. 方法区/元数据区 /元空间 :放的是类加载之后的类对象 【静态变量】
JVM的类加载机制
类加载的过程:把 .class 文件加载到内存,得到类对象这样的过程
分为五步:加载,验证,准备,解析,初始化
1.加载 :
找到.class文件,并读文件内容
2.验证:
验证选项:
文件格式验证
字节码验证
符号引用验证...
.class文件有明确的数据格式(二进制的)
3.准备:
给类对象分配内存空间(未初始化的空间,数据全是0的,类对象中的静态变量啥的全是0,相当于租了个毛坯房)
4.解析:
针对字符串常量进行初始化【字符串常量池中的符号引用替换为直接引用的过程】
相当于之前是只知道彼此之间的相对位置,这时候字符串常量就是符号引用,真正加载到内存中就会把字符串常量填充到内存的特定位置上,字符串之间的相对位置还是一样,但是这些字符串有了自己真正的内存地址。此时的字符串就是直接引用(java中的普通引用)
【打个比方:开学排座位的时候,学校通知按照学号排,你只知道你前面一个位置是小明的,但是你不知道你具体坐在第几排,排好座位以后你知道了自己在第三排,小明在第二排,你们知道了自己的具体位置,并且你们之间的相对位置没变,小明还是在你前面一个。】
5.初始化
针对类对象进行初始化(初始化静态成员,执行静态代码块,类要是有父类还需要加载父类)
注意:类加载这个动作不是JVM一启动就把所有.class文件都加载的,整体是一个“懒加载”的策略(懒汉模式),非必要不加载。
所谓的“必要”情况:
- 创建了这个类的实例
- 使用了这个类的静态方法/属性
- 使用了子类,会触发父类的加载
双亲委派模型
【说在前面:双亲其实只有一个准确的说是父亲,(都是翻译的锅)】
JVM中加载类,需要用到一组特殊的模块,类加载器,JVM内置了三个类加载器(程序员也可以定义自己的类加载器)
- BootStrap ClassLoader:启动类加载器:加载 JDK 中 lib ⽬录中 Java 的核⼼类库,即$JAVA_HOME/lib⽬录
- Exttension ClassLoader :扩展类加载器。加载 lib/ext ⽬录下的类,非标准的但是是Sun/Oreacle 扩展的库的类库
- Application ClassLoader:应⽤程序类加载器。负责加载项目中自己写的类,以及第三方库中的类。
具体加载一个类的过程:
注意:双亲委派模型是可以打破的,比如tomcat就没有遵守
JVM中的垃圾回收机制 ( GC)
垃圾回收机制主要是帮助程序员自动释放内存的
【这里说的是基本思想,JVM具体是怎么实现的,这个问题很复杂,就不讨论了】
程序计数器,就是单纯存地址的整数,跟随线程一起销毁
栈,方法调用完毕,方法的局部变量也随着线程一起销毁
元数据区,存的是类对象,很少会“卸载”
所以,堆就是GC的主要目标
GC是以对象为单位进行释放的,【说是释放内存,其实就是释放对象】
GC主要分为两个阶段:
找谁是垃圾,释放内存
找谁是垃圾
一个对象,如果后序不在用了,就可以认为是 垃圾
java中使用一个对象,只能通过引用,所以java单纯地通过引用有没有指向来判定是不是垃圾
通常有两种方法:引用计数,可达性分析
1.引用计数:
给对象安排一个额外的空间,保存一个整数,表示该对象有指向【java没有使用这个方法,Python,php用了】
2.可达性分析
把对象之间的引用关系理解成一个树形结构,从一些特殊的起点出发。进行遍历,只要遍历能访问到的对象就是“可达”的,再把“不可达”的当做垃圾即可。
可达性分析关键要点进行上述遍历需要有 “起点”
- 栈上的局部变量(每个栈的每个局部变量都是起点。)
- 常量池中引用的对象
- 方法区中静态成员引用的对象。
可达性分析总的来说就是从所有的gc roots的起点出发,看看该对象里又通过引用能访问哪些对象,顺藤摸瓜的,把所有可以访问的对象都给变了一遍。便利的同时把对象标记成可达,剩下的自然就是不可达。
可达性分析克服了引用技术的两个缺点。但是也有自己的问题。
- 因为要遍历所有对象,需要消耗更多的时间,因此某个对象成了垃圾,也不能第一时间发现。
- 在进行可达性分析的时候要顺藤摸瓜,一旦这个过程中当前代码中引对象的引用关系发生了变化,就会出现问题。【STW问题(让其他业务线暂停),现在已经能很好的应对了】
释放内存
有三种策略:标记清除,复制算法,标记整理
标记清除
缺点:内存碎片化
复制算法
缺点:
内存空间利用率低,复制成本高
标记整理
上述三种策略都有缺点,所以,实际上 JVM 的实现思路,是结合了上述几种思想方法,针对不同情况采取不同策略,产生了 :分代回收思想
垃圾收集器
如果说上⾯我们讲的收集算法是内存回收的⽅法论,那么垃圾收集器就是内存回收的具体实现。
【本文就不介绍了。】
⾯试题 :
请问了解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倍以上
以上就是本文分享的主要内容,对你有帮助的话,可以点个赞哦~~