深入解析:Java JVM --- JVM内存区域划分,类加载,GC垃圾回收
文章目录
- JVM
- JVM内存区域划分
- 类加载
- 类加载的基本流程
- 双亲委派模型
- GC垃圾回收
- GC的过程
JVM
- JVM主要是面试要考察,它是Java虚拟机中的内容
- 面试中经常考的主题:
(1) JVM 内存区域划分
(2) JVM 中类加载的过程
(3) JVM 中垃圾回收机制
JVM内存区域划分
一个运行起来的Java进程就是一个JVM虚拟机,就需要从操作系统申请一大块内存,就会把这个内存,划分成不同的区域,每个区域都有不同的作用。
这个区域就类似租一个写字楼,进行装修
(这个大的场地,相当与申请了一大块内存空间)
JVM 申请了一大块内存之后,也会划分成不同的内存区域
方法区(jdk 1.7及其之前)/ 元数据区(jdk 1.8 开始往后)
类对象(.class文件,加载到内存之后,就成了类对象了)就是这里存储的内容,就堆
代码中new的对象就是这里存储的内容,就
(占据空间最大的区域)栈
这里存储的内容,就是代码执行过程中,方法之间的调用关系
比如 a->b->c->d,a方法调用b方法,b方法调用c方法,以此类推
4. 工具计数器
比较小的空间,首要就是存放一个地址,表示下一条要执行的指令,在内存中的哪个地方(方法区里,每个方法,里面的指令,都是以二进制的形式存在的,保存到对应的类对象中的)
(1) 刚开始调用方法,程序计数器,记录的就是方法的入口地址。随着一条一条的执行指令,每记录一条,程序计数器的值都会自动更新,去指向下一条指令
(2) 倘若是一个顺序执行的代码,下一条指令就是把指令地址进行递增(栈顶是低地址,栈底是高地址)
条件/循环代码,下一条指令就可能会跳转到一个比较远的地址就是(3) 如果
常见面试题:
处于内存中的哪个区域中?就是给你一个代码,问你某个变量,
有一个疑问,容易让大家搞错,基本类型,没关系~就是变量处于哪个空间上,和变量是不是引用类型,是不
每个线程都有自己私有的栈空间
有自己私有的栈空间的就是在C++中行访问别的线程的空间,所以在C++中不是私有的,而在Java中
5. 内存区域划分
(1) 理解这些区起到的作用
(2) 给你一个代码,给你一个变量,问你这个变量是在哪个区的问题
类加载
- 类加载:java代码会被编译成.class文档(含有了一些字节码),java程序要想运行起来,就需要让jvm读取到这些.class文档,并且把里面的内容,构造成类对象,保存到内存的方法区中。
jvm就会操作程序计数器去类对象中读取指令,并执行了
调用技巧。就需先知道每个方法,编译后生成的就是所谓的执行代码,就指令都是什么
类加载的基本流程
- 书上和官方文档把该类加载的过程,核心分成了5个步骤(或者是三个步骤,把中间三个步骤合为一个步骤了)
要想学习这个,最好是去读源码,openjdk是开源的,官方的jdk(oracle)是不开源的
八股文来背了就是下面的东西都要当做
(1) 加载:找到 .class 文件,打开资料,读取文件内容
往往代码中,会给定某个类,全限定类名,例如:java.lang.String,java.util.ArrayList,jvm就会根据这个类名,在一些指定的目录范围内,查找
(2)验证:.class文件是一个二进制的格式(某个字节,都有某些特定含义的),就需要验证你当前读到的该格式是否符合要求
Attributes:注解
(3)要构造出类对象)就是准备:给类对象分配内存空间(最终的目标,
全0的)就是这里只是分配内存空间,还没有初始化呢。此时该空间上的内存的数值,就是全0的。(此时如果尝试打印类的static成员,就
(4)解析:针对类对象中涵盖的字符串常量进行处理。进行一些初始化操作。java代码中用到的字符串常量,在编译之后,也会进入到.class文件中
举个栗子:
期权就是符号引用,股权就是直接引用,公司没有上市就是期权(在文件中),公司上市了就是股权(在内存中)
5. 初始化:针对类对象进行初始化
把类对象中需要的各个属性都设置好,还应该初始化好static成员,还应该执行静态代码块,以及还可能需要加载一下父类(比如子类继承了父类的)
以上内容,要求我们得熟练背诵,面试的时候很大概率会遇到原题
双亲委派模型
- 双亲委派模型:属于类加载中,第一个步骤(加载中的第一个步骤),其中的一个环节
- 作用:负责根据全限定类名,找到 .class文件
这里的双亲,指的是parent该单词,双亲之一,parents才是真正的双亲。
实际上这里翻译成父亲委派模型更合适一些
JVM中内置了三个类加载器:
3. 类加载的过程(找 .class文件的过程)
4. 搞双亲委派模型的主要原因:是为了保证优先级,让标准库优先查找,再让扩展库找,最后让自己写的类和第三方库找
所谓的双亲委派模型,其实就是一个简单的查找优先级难题,只不过面试中往往考它,是因为它有一个高大上的名字
- 不可打破的。如果我们自己写一个类加载器,不一定非要遵守上述的流程。就是双亲委派模型,也不
比如:tomcat里,加载webapp的时候就是用的自定义的类加载器。就只能在webapp指定的目录中找,这里找不到,就算了,直接抛异常,不会去标准库啥的里面去找了!!
- 可以从最高级的那个加载器开始加载吗?这是能够的,JVM中用了递归的方法,效果都是一样的
GC垃圾回收
在C语言和C++中都有动态申请内存,如果没有及时地释放空间,那么就会产生内存泄漏的风险。
在C++中即使调用了delete,还是有可能出现内存泄漏的问题
否要继续启用,如果不用了就会被回收就是GC垃圾回收机制的好处:JVM会自动判定这个内存
C++为什么不引入GC ?
STW问题就是中间突然卡顿一下堆上的对象就是GC真正要释放的
GC的过程
gc可以理解成两个大的步骤:
1.找到垃圾
2.释放垃圾
垃圾描述了一种客观的状态,没有主观贬低的意思。就是:指的是一个不再使用的对象,只
如果面试官让你介绍垃圾回收,你可能介绍引用计数。如果让你介绍java的垃圾回收,介绍引用计数就不合适了
- 找到垃圾
在GC的圈子中,有两种主流的方案:
(1) 引用计数[Python,PHP]
深入理解java虚拟机中也介绍了引用计数
引用计数描述这个对象有几个引用指向它,引用计数为0,就是没有对象引用了,就垃圾回收了
为什么Java不使用引用计数?
引用计数,存在两个核心的问题
1.比较浪费内存
2.引用计数机制,存在循环引用的疑问
循环引用:存在一个a引用一个对象,一个b引用一个对象,然后a里的的t引用b对象,b里面的t引用a对象,再把a对象和b对象置为null,销毁两引用。那么这两个对象中的t互相引用,要拿到a对象中的t,要求b对象中的t,要拿到b对象中的t,需要a对象中的t。这就是循环引用。
(2) 可达性分析[Java]
可达性分析:本质上是时间换空间这样的手段。有一个 / 一组线程,周期性的扫描我们的代码中所有的对象。
举个例子:
二叉树的遍历
可达性分析,出发点有很多,所有的局部变量,还有常量池中引用的对象,还有方法区中的静态引用类型引用的变量…就是不仅仅
这些许可作为出发点的统称为 GCRoots
这里的可达性分析,都是周期性进行的(可达性分析比较消耗系统资源,开销比较大垃圾,随着代码的执行,这个就变成了垃圾)就是,时间上消耗比较大),当前某个对象是否是垃圾,是随着代码的执行,会发生改变(可能当前这个不
通过总结:能够到达的点进行全部的遍历,并且是周期性地遍历
- 如何回收垃圾
三种主要的思路
(1) 标记清除,比较简单粗暴的释放方式(会产生大量的内存碎片,导致后续有足够的空间,但无法申请)
(2) 复制算法(为了解决标记清除)
经过复制的方式,把有效的对象,归类到一起。再统一释放剩下的空间
(先把空间砍一半,比如左边总共有5个对象,但是有2个对象是垃圾,那么要把有效的3个对象复制到右边,然后把左边5个对象都释放掉)
(3) 标记整理
既能够解除内存碎片挑战,又能处理复制算法中利用率的难题
(比如有6个对象,3个对象有用,3个对象是垃圾,把有效的对象都搬运到前面来,后面是垃圾的一起释放掉)
上述基础思路的结合体,让不同的方案,扬长避短就是实际上,JVM采用的释放思路,
上述结合(JVM释放)的过程:
分代回收,对象能活过的GC扫描轮次越多,就是越老
新生代,主要使用复制算法进行回收有效的对象,老年代,主要使用标记整理的方式回收有效的对象(标记清除,十分不好用,并没有真正采用)
分代回收,是JVM中主要的回收的思想方法,但是在垃圾回收器的具体建立的时候,可能还有一些调整和优化
其实,分代回收,就和找工作的流程是一样的,都会经历上述的新生代,幸存区和老年代的过程
核心掌握怎么找垃圾,怎么释放垃圾就可以了
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/927241.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!