文章目录
JVM的类加载过程
JVM的类加载过程包括加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)五个阶段,具体如下:
-
加载(Loading):加载是类加载过程的第一个阶段,它的主要任务是通过类加载器将类的字节码文件加载到内存中,并生成一个代表该类的 java.lang.Class 对象。类加载器会根据类的全限定名来定位并读取类文件,然后将类的字节码数据转换为方法区的运行时数据结构。
-
验证(Verification):在验证阶段,虚拟机将对加载的类进行各种验证,以确保类文件的字节流符合虚拟机的规范要求,防止恶意代码或错误的字节码文件影响虚拟机的正常运行。
-
准备(Preparation):在准备阶段,虚拟机为类的静态变量分配内存,并设置默认初始值,这些变量存储在方法区中。注意,此时不会为实例变量分配内存,实例变量会在对象实例化时随着对象一起分配在堆内存中。
-
解析(Resolution):解析阶段是将常量池中的符号引用替换为直接引用的过程。解析阶段可能在初始化之前或初始化过程中进行。
-
初始化(Initialization):初始化阶段是类加载过程的最后一个阶段,此阶段是真正执行类中定义的Java程序代码(静态变量赋值、静态代码块等)的阶段。当一个类被初始化时,其父类也会被初始化,但接口并不会被初始化。类初始化阶段是线程安全的,多个线程同时初始化一个类时,只会有一个线程执行初始化,其他线程会被阻塞。
总的来说,类加载过程是JVM将类加载到内存并准备好供程序运行所需的各种数据结构的过程,它确保了Java程序的正确性和安全性。
JAVA内存泄露
内存泄露的常见原因
- 资源未释放
程序中使用的资源(比如文件、数据库连接、网络连接等)未被正确释放或关闭,这些资源所占用的内存将无法被垃圾回收机制释放。如果程序中频繁创建资源却不释放或者忘记释放资源,将会导致内存泄漏问题。 - 内部类持有外部类
如果有地方引用了这个非静态内部类,会导致外部类也被引用,即使外部类已经没有其他地方在使用了,垃圾回收时也无法回收这个外部类 - ThreadLocal的内存泄露
ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
ThreadLocal正确的使用方法
每次使用完ThreadLocal都调用它的remove()方法清除数据
将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。
内存泄露的影响
资源不释放,随着时间增加,内存占用也会增加,导致系统性能下降,出现卡顿或响应缓慢等问题系统资源耗尽最终导致内存溢出。
问题排查
如果线上出了问题,首先判断是业务问题还是整个系统的问题。如果是业务问题,就去看应用的日志等进行排查。如果出现了如下问题,就可能是整个系统的问题:
- 大量接口都很慢
- 页面打不开
- 是否CPU占用过高
- 是否内存占用过高
- 是否磁盘占用过高
- 是否网络故障
- 查看后台日志
- 是否是数据库问题(比如:索引失效、死锁)
- 是否是垃圾回收导致
- 是否死锁等
JVM的调优
- Xms
初始堆大小。 - Xmx
最大堆大小。
一般将Xms和Xmx设为一样的值,若-Xms比较小,又需要初始化很多对象,jvm就必须反复增加内存。一样大也可避免每次垃圾回收完成后JVM重新分配内存。 - Xss
线程的栈的大小。 - XX:NewSize=n
设置年轻代大小 - Xmn
设置年轻代初始大小和最大大小。增大年轻代后,会减小年老代大小,此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
等效于: 使用 -XX:NewSize 设置初始化大小并使用-XX:MaxNewSize 设置最大大小。 - XX:NewRatio=n
设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 - XX:SurvivorRatio=n
年轻代中Eden区与两个Survivor区的比值。 - XX:MetaspaceSize=n
元空间大小。 - XX:MaxMetaspaceSize=n
最大元空间大小。
java四种引用类型
- 强引用
强引用是使用最普遍的引用,我们写的代码,99.9999%都是强引用
只要某个对象有强引用与之关联,这个对象永远不会被回收
,即使内存不足,JVM宁愿抛出OOM,也不会去回收。 - 软引用
只有在内存不足时,JVM才会回收
该对象。
当内存不足时,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用包裹的对象给干掉。 - 弱引用
不管内存是否足够,只要发生GC,弱引用就会被回收。
- 虚引用
无法通过虚引用来获取对一个对象的真实引用。
虚引用必须与ReferenceQueue一起使用。当GC准备回收一个对象时,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。