Java基础 JVM 垃圾回收classloader
Posted danfengw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础 JVM 垃圾回收classloader相关的知识,希望对你有一定的参考价值。
JVM 相关问题
1 Jvm(java虚拟机运行时内存) 包括哪些区域?
包括线程私有的区域 和线程公有区域
线程私有区域: 程序计数器、虚拟机栈、本地方法栈
线程公有区域:堆、方法区
程序计数器:当前线程正在运行的字节码指令地址(行号)
虚拟机栈(OutOfMemoryError 、StackOverflowError):存储运行程序所需要的数据 指令 返回地址,每个方法执行的时候都会创建一个栈帧,栈帧中存储了 局部变量 、操作数栈(方法中调用方法的时候)、动态链接(涉及多态的时候),返回地址,每一个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的 过程。
本地方法栈:本地方法栈功能与虚拟机栈类似,区别是虚拟机栈是为虚拟机执行java方法,而本地方法栈则是执行naitive方法
堆(OutOfMemoryError): 是JVM所管理的内存中最大的一块。唯一目的就是存放实例对象,几 乎所有的对象实例都在这里分配。Java堆是垃圾收集器管理的主要区域,因此很多 时候也被称为“GC堆”。
方法区(OutOfMemoryError): 已加载的类信息、常量、静态常量 、编译后的代码等数据
2 字符串常量池在Java内存区域的哪个位置?
在JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中;
在JDK7.0版本,字符串常量池被移到了堆中了。至于为什么移到堆内,是由于方法区的内存空间太小了
可能发生的异常标记在()内
JMM
1 谈谈Java 的内存模型
java的内存模型主要指的是JMM,它主要划分为主内存和工作内存的配合关系,然后主内存与工作内存分别对应着,jvm的堆、方法区 以及 程序计数器 虚拟机栈 本地方法栈等。此外jmm有三个特征: 原子性 可见性 有序性
2 原子性 可见性 有序性
原子性:一个操作不能被打断,要么全部执行完毕,要么不执行
可见性:一个线程对共享变量做了修改之后,其他的线程立即能够看到(感知到)该变量这种修改(变化)。
有序性:在本线程内观察,操作都是有序的;如果在一个线程中观察另外一个线程,所有的操作都是无序的。
3 Java 中实现可见性的关键字
volatile、synchroinzed、final
https://blog.csdn.net/pengzhisen123/article/details/80270062
4 volatile 的 使用场景3个
垃圾回收
讲到jvm 堆是gc的主要区域,接下来就可以涉及一下垃圾回收了
概念相关:
MinorGC MajorGC FullGC
- Minor GC
- Minor GC指新生代GC,即发生在新生代(包括Eden区和Survivor区)的垃圾回收操作,当新生代无法为新生对象分配内存空间的时候,会触发Minor GC。因为新生代中大多数对象的生命周期都很短,所以发生Minor GC的频率很高,虽然它会触发stop-the-world,但是它的回收速度很快。
- Major GC
- Major GC清理Tenured区,用于回收老年代,出现Major GC通常会出现至少一次Minor GC。
- Full GC
- Full GC是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。Full GC不等于Major GC,也不等于Minor GC+Major GC,发生Full GC需要看使用了什么垃圾收集器组合,才能解释是什么样的垃圾回收
对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。
大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,知道达到阀值对象进入老年区。
动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。
回收器
串行(Serial)回收器是单线程的一个回收器,简单、易实现、效率高。
并行(ParNew)回收器是Serial的多线程版,可以充分的利用CPU资源,减少回收的时间。
吞吐量优先(Parallel Scavenge)回收器,侧重于吞吐量的控制。
并发标记清除(CMS,Concurrent Mark Sweep)回收器是一种以获取最短回收停顿时间为目标的回收器,该回收器是基于“标记-清除”算法实现的。
1 垃圾回收基础相关
jdk1.7以前 分为 新生代 、老年代 、永久代,1.8开始永久代被替换成元空间
新生代:又分成Eden区 、From Survior 区 、To Survior 区 ,一般按照8:1:1的比例划分3个区域。该区域对象每进行一次gc后存活下来,年龄就会+1,达到15次以后,就会被放到老年代。------复制算法
老年代:存放生命周期比较长的对象,还有当对象内存较大时会直接放到老年代上,还有就是放置新生代年龄>15的对象,以及Survior中存放不下的对象也会被放到老年代。---------标记清除–整理算法
新生代的gc是—MinorGC
老年代的gc是----Major GC 通常情况下与FullGC 等价,因为每次MajorGC出现会伴有至少一次的MinorGC
出发FullGC 条件:
(1)老年代空间不足
(2)永久代空间不足
(3)CMS GC 时出现promotion failed 或者concurrent mode failed
(4)MinorGC 晋升到老年代的平均大小大于老年代剩余空间
(5)调用System.gc() 还有一条不列举了
2 垃圾回收标记算法
(1)引用计数算法
给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用 失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能被再使用的。
主流的JVM里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解 决对象间的互循环引用的问题。
(2)可达性分析算法
当一个对象到GC Roots没有任何引用链相连时(就是从 GC Roots 到这个对象是不可达),则证明此对象是不可用的。所以它们会被判定 为可回收对象
3 垃圾回收算法
(1)标记清除算法 (从根集合进行扫描,对存活的对象进行标记,对堆内存从头到尾遍历,清除没有被标记的对象)
缺点:碎片化
(2)复制算法(分为对象面和空闲面,对象创建在对象面上面,会将存活的对象从对象面复制到空闲面,清除对象面)
优点:解决碎片化、适合对象存活率低的场景
缺点:对生命周期比较长的对象有些吃力
(5)标记整理算法(从根集合进行扫描,对存活的对象进行标记,将所有存活的对象按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收)
优点:避免了内存的不连续,不用设置两块内存互换,适用于存活率高的场景
(4)分代收集算法 (垃圾收集算法的组合拳,按照对象生命周期的不同划分区域以采用不同的算法,提高gc效率)
新生代 (复制算法)MinorGC 原因:新生代生命周期短
老年代 (标记清除整理算法)FullGC
4 Minor GC和Full GC的区别
Minor GC:指发生在新生代的垃圾收集动作,该动作非常频繁。
Full GC:是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。Full GC不等于Major GC,也不等于Minor GC+Major GC。
5 垃圾回收中GCRoot 的区域是哪些?
在Java语言中,可以作为GC Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象;
- 方法区中类静态属性引用的对象;
- 方法区中常量引用的对象;
- 本地方法栈中JNI(即一般说的Native方法)引用的对象;
- 活跃线程的引用对象
总结就是,方法运行时,方法中引用的对象;类的静态变量引用的对象;类中常量 引用的对象;Native方法中引用的对象
6 java中会发生oom的地方有哪些? 虚拟机栈 、 本地方法栈、堆、 方法区
7 四种引用
强引用: 就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类 的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
软引用: 用来描述一些还有用但并非必须的对象。在系统将要发生内存溢出异常之 前,将会把这些对象列进回收范围之中进行第二次回收。可以用来实现高速缓存。
弱引用: 用户描述非必须对象的。被弱引用关联的对象只能生存到下一次垃圾收集 发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用 关联的对象。
虚引用: 一个对象是否有虚引用存在,不会决定对象的生命周期,任何时候都可能被垃圾收集器回收。
8 垃圾收集器都有哪些
新生代垃圾收集器:
(1)Serial收集器 : 采用复制算法,单线程收集,进行垃圾收集时,必须暂停所有工作线程,简单高效,jvm在Client模式下默认的年轻代收集器。
(2)ParNew收集器:采用复制算法,区别于Serial的是多线程收集,其余行为与特点与serial收集器一样。单核执行效率不如Serial,在多核下才有优势
(3)Parallel Scavenge :复制算法,也是多线程,但是它比起关注用户停顿时间,更关注系统的吞吐量
(4)G1 收集器(GarGarbage First):将整个java堆内存划分成多个大小相等的Region,年轻代与老年代不再物理隔离
特点:并发和并行,分代回收,空间整合、可预测的停顿
老年代垃圾收集器:
(1)CMS:标记清除算法,尽量不影响用户线程
(2) Serial Old :标记整理算法,单线程收集,进行垃圾收集时,必须暂停所有工作线程,简单高效,jvm在Client模式下默认的年轻代收集器。
(3)Parallel Old:多线程,吞吐量优先
(4)G1 收集器(GarGarbage First):将整个java堆内存划分成多个大小相等的Region,年轻代与老年代不再物理隔离
特点:并发和并行,分代回收,空间整合、可预测的停顿
ClassLoader 相关
1 jvm如何加载.class文件
jvm 主要由classloader 、Runtime Data Area 、Execution Engine 、Native Interface 四个部分组成,通过classloader 将符合格式要求的class 文件加载到内存,并通过execution engine对相关命令解析并将class 加载到内存中
2 类的加载
加载,验证,准备,解析,初始化,使用和卸载。其中验证,准备,解析3个部分 统称为连接。
这7个阶段发生顺序如下图:
加载:
1.通过一个类的全限定名来获取定义此类的二进制字节流
2.将这个字节流所代表的静态存储结构转换为方法区内的运行时数据结构
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种 数据的访问入口。
验证:
是连接阶段的第一步,目的是为了确保Class文件的字节流中包含的信息符合当前 虚拟机的要求,并且不会危害虚拟机自身的安全。
包含四个阶段的校验动作
a.文件格式验证 验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。
b.元数据验证
对类的元数据信息进行语义校验,是否不存在不符合Java语言规范的元数据信息
c.字节码验证
最复杂的一个阶段,主要目的是通过数据流和控制流分析,确定程序语义是合法 的,符合逻辑的。对类的方法体进行校验分析,保证被校验类的方法在运行时不会 做出危害虚拟机安全的事件。
d.符号引用验证
最后一个阶段的校验发生在虚拟机将符号引用转换为直接引用的时候,这个转换动 作将在连接的第三个阶段——解析阶段中发生。
符号验证的目的是确保解析动作能正常进行。
准备:
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段。这些变量所使用的 内存都将在方法区中分配。只包括类变量。初始值“通常情况”下是数据类型的零 值。
“特殊情况”下,如果类字段的字段属性表中存在ConstantValue属性,那么在准备阶 段变量的值就会被初始化为ConstantValue属性所指定的值。
解析:
虚拟机将常量池内的符号引用替换为直接引用的过程。
“动态解析”的含义就是必须等到程序实际运行到这条指令的时候,解析动作才能进 行。相对的,其余可触发解析的指令都是“静态”的,可以在刚刚完成加载阶段,还 没有开始执行代码时就进行解析。
初始化:
类加载过程中的最后一步。
初始化阶段是执行类构造器 clinit() 方法的过程。
clinit() 方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句 块中的语句合并产生的。
与类的构造函数不同,它不需要显示地调用父类构造器,虚拟机会 保证在子类的 方法执行之前,父类的 clinit() 方法已经执行完 毕。
简单地说,初始化就是对类变量进行赋值及执行静态代码块。
3 双亲委派模式
比如我们想加载CustomClassLoader 先去看该类是否被加载过,如果被加载过就直接返回该类,如果没有会询问AppClassLoader,如果AppClassLoader 加载过就会直接返回,否则会询问AppClassLoader的上一层Extension ClassLoader
,如果加载过就直接返回,否则 向Bootstap ClassLoader询问,是否加载过,加载过直接返回,没有加载过就会从制定
4 为什么使用双亲委派机制去加载类
避免多份同样字节码的加载,节约资源
5 怎样自己写一个classLoader
(1)写一个类继承ClassLoader
(2)复写findClass()方法。
(3)在findClass()方法中调用defineClass()。
https://blog.csdn.net/lioncatch/article/details/106013246
6 类加载验证 初始化 后何时卸载
如果有下面的情况,类就会被卸载:
1、该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
2、加载该类的ClassLoader已经被回收。
3、该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
如果以上三个条件全部满足,jvm就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java类的整个生命周期就结束了。
7 两个ClassLoader加载同一个类,加载出来的是同一个class文件吗?
一个类或者接口并不是单单由它的名称确定的,而是由它的二进制名称以及它的定义类加载器共同确定的。https://www.cnblogs.com/SanjiApollo/p/12839682.html
8 java的classLoader有哪些
Bootstrap ClassLoader、 Extension ClassLoader 、AppClassLoader
9 类加载器是用什么加载的?
以上是关于Java基础 JVM 垃圾回收classloader的主要内容,如果未能解决你的问题,请参考以下文章