目录
JVM运行时的数据区(内存结构):
线程独享:
线程共享:
什么时候会内存溢出
JVM有哪些垃圾回收算法
GC如何判断对象可以被回收
典型的垃圾回收器
CMS:
G1:
类加载器和双亲委派机制:
类加载器
双亲委派机制
JVM中有哪些引用
虚引用
类加载的过程
JVM类初始化的顺序
对象的创建过程
对象头中有哪些信息
JVM的内存参数
GC的回收机制与原理
堆内存溢出:
栈溢出:
标记清除算法:标记不需要回收的对象(首先会进行标记阶段。从根对象(如全局变量、栈中的引用等)开始,通过可达性分析或其他方式,遍历对象图谱,将所有与根对象可达的对象标记为存活对象),然后清除没有标记的对象,这种方式会造成许多内存碎片。
复制算法:将内存将内存分为两块,只使用一块,进行垃圾回收的时候,先将存活的对象复杂到另一块区域,然后在清空之前的区域。(新生代中的from区和to区用的就是类似这种算法)
标记整理算法:与标记清除算法类似,但是在标记之后,将存活的对象向一方移动,然后清除边界外的垃圾对象。应用于老年代的垃圾回收
引用计数法:为每个对象添加引用计数器,当引用个数为0的时候判定为可以被回收,但存在有两个对象相互引用导致无法回收的问题
可达性分析算法:从GCRoots开始向下搜索,搜索过的路径称为引用链,对于GCRoots没有任何引用的对象,则判定可以被回收
什么是GCRoots:
在Java中,以下几种情况可以被定义为GCRoots:
以最小的停顿时间为目标,只运行在老年代的垃圾回收器,使用标记清除算法,可以并发收集(标记和清除阶段)。
CMS回收器采用了一种并发标记清除算法,具有以下几个主要特点:
需要注意的是,CMS回收器适用于具有大内存、长时间运行的应用程序,并且强调减少停顿时间。但由于并发执行和空间碎片的特点,它可能会导致一些额外的CPU开销,并且在极端情况下可能会触发Full GC。
JDK1.9之后的默认的垃圾回收器,注重响应速度,支持并发,采用标记整理+复制算法回收内存,使用可达性分析判断对象是否应该被回收。
G1回收器具有以下几个主要特点:
G1回收器适用于具有大内存、需要低延迟和可预测停顿时间的应用程序。相对于CMS回收器,G1回收器提供了更好的吞吐量和停顿时间控制。但与此同时,它也引入了一些额外的开销,如内存开销、CPU开销和GC负载。
类加载器负责从文件系统、网络或其他来源加载类的字节码,并将其转换为JVM可以理解的格式。JVM支持多个类加载器,每个加载器都负责加载特定类型的类。
从父加载器到子类加载器分别为:
BootStrapClassLoader(启动类加载器) 加载路径——JAVA_HOME/jre/lib
ExtensionClassLoader(扩展类加载器) 加载路径——JAVA_HOME/jre/lib/ext
ApplicationClassLoader (应用程序类加载器) 加载路径——classPath
双亲委派机制是类加载器的一种工作方式,它通过层级关系进行加载类的过程。当一个类加载器需要加载一个类时,它首先将这个任务委派给它的父类加载器,父类加载器再将任务委派给它的父类加载器,直至最终委派给顶层的启动类加载器。只有当父类加载器无法加载这个类时,子加载器才会尝试自己加载。
优势
安全性: 假设有一个恶意代码想要替换Java核心API类中的某个类,比如java.lang.String
。由于双亲委派机制的存在,该加载请求会首先被委派给启动类加载器进行处理。启动类加载器负责加载核心Java API,并将其保护起来,以防止被篡改或替换。因此,即使存在恶意代码尝试加载java.lang.String
类,由于该类已由启动类加载器加载,恶意代码的加载请求将被拒绝。
避免重复加载: 假设我们有两个不同的类加载器,分别是ClassloaderA
和ClassloaderB
,它们都可以加载同一个类com.example.MyClass
。如果没有双亲委派机制,当需要加载com.example.MyClass
时,ClassloaderA
和ClassloaderB
可能会分别加载自己的版本,导致在JVM中存在多个com.example.MyClass
类的副本,可能会引发类冲突和不一致的问题。而通过双亲委派机制,当ClassloaderA
和ClassloaderB
都遵循委派机制,它们都会将加载请求委派给父类加载器,最终由启动类加载器加载该类。这样,只有一个版本的com.example.MyClass
类会被加载,避免了重复加载和冲突。
存在性检查: 当一个类加载器需要加载某个类时,它会先询问父类加载器是否已经加载过该类。如果父类加载器已经加载了该类,那么子类加载器就无需再次加载,直接使用父类加载器加载的类。这样做可以提高加载效率,避免不必要的重复加载操作。例如,当一个应用程序使用Class.forName("com.example.MyClass")
动态加载某个类时,首先会询问应用程序类加载器(Application Class Loader)是否已经加载了该类。如果已经加载,则可以直接使用,而不需要再次加载。
在JVM(Java虚拟机)中,引用是指对对象的间接访问。引用允许我们操作和处理对象,而无需直接访问对象本身。引用是一种对内存中对象的标识,通过引用可以定位和操作存储在堆中的对象。
引用在Java中有不同的类型,包括强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。它们的主要区别在于对垃圾回收的影响和生命周期的长短。
当一个对象只有虚引用指向它时,无论何时垃圾回收器决定回收该对象,它都可以立即进行回收。在回收对象之前,垃圾回收器会将这个对象放入一个特殊的引用队列中。
使用虚引用的好处是,我们可以通过检查引用队列来确定哪些对象已经被回收。这样,我们可以在对象被回收时采取一些额外的操作或记录。例如,我们可以在对象被回收时进行资源清理或日志记录。
要注意的是,虚引用本身并不能保持对象的存活,也不能通过虚引用来访问对象。它仅仅是在对象被回收前提供了一个通知的机制,以便我们可以观察和处理对象的回收过程。
所以,虚引用的主要作用是帮助开发者了解对象的回收情况,并在需要时执行一些附加的操作。
(1)加载:将字节码通过二进制的形式转化到方法区中的运行数据区当中。
(2)连接:
(3)初始化:执行类构造器(不是常规的那种类的构造方法),为静态变量赋初值并初始化静态代码块。
父类静态代码块和静态成员变量-》
子类静态代码块和静态成员变量-》
父类代码块和普通成员变量-》
子类代码块和普通成员变量-》
子类构造方法
(1)检查类是否被加载,如果没加载就先加载类
(2)为对象在堆中分配内存,使用CAS方式分配,防止在为A进行内存分配的时候,执行当前地址的指针还没有来得及修改,对象B就拿来分配内存(让A分配完了再让B分配)
(3)初始化,将对象中的属性都分配为0值或者为null
(4)设置对象头
(5)为属性赋值和执行构造方法
1.Markword
2.类指针KlassPointer
-Xmx[]:堆空间最大内存
-Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的
-Xmn[]:新生代的最大内存
-xx:[survivorRatio=3]:Eden区与from+to区的比例为3:1,默认为4:1
-xx[use 垃圾回收器名称]:指定垃圾回收器
-xss:设置单个线程栈大小
一般设堆空间为最大可用物理地址的百分之80
GC的目的实现内存的自动释放,使用可达性分析算法判断对象是否可被回收,采用了分代回收的思想,将堆分为了新生代、老年代;新生代中采取了复制算法,老年代中采用了标记整理算法,当新生代内存不够的时候发生Minor GC,老年代内存不够的时候发生Full GC