C/C++,无自动回收机制,对象不用时需要手动释放,否则积累导致内存泄漏:
Java、C#、Python、Go等支持对不再使用的对象进行回收:
Java引入GC机制,主要负责对堆上的内存进行回收,简化了对象的释放,但同时也丧失了回收的及时性,因为回收操作不再又开发者做了。
线程不共享的部分,随着线程的创建而创建,随着线程的死亡而销毁,不会发生内存泄漏。且栈里的每个栈帧,在方法执行完后直接弹栈并释放内存,都不用等到线程死亡。
添加JVM参数:
-verbose:gc
可查看回收日志
即回收不再使用的类,对应类生命周期里的卸载阶段。类A被卸载,需同时满足:
URLClassLoader loader = new URLClassLoader(new URL[] {new URL("file:D:\\lib\\")});
Class<?> clazz = loader.loadClass("com.plat.domain.A");
Object a = clazz.newInstance();
a = null;
clazz = null;
loader = null;
Demo代码如下,循环体中创建的变量,一轮结束后自动没用,不用重复 o = null
public class Demo2 {
public static void main(String[] args) {
try {
ArrayList<Class<?>> classes = new ArrayList<>();
ArrayList<ClassLoader> loaders = new ArrayList<>();
ArrayList<Object> objs = new ArrayList<>();
while (true) {
//URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("file:D:\\tmp\\")});
BreakClassLoader1 classLoader = new BreakClassLoader1(); //自定义类加载器
classLoader.setBasePath("D:\\springboot\\jvm\\target\\classes\\");
Class<?> clazz = classLoader.loadClass("com.plat.app.Demo1");
Object o = clazz.newInstance();
//循环体中创建的变量,一轮结束后自动没用,不用重复 o = null 、clazz = null 、classLoader = null
/*
打开这里的任意一句,就有一个对应的对象被引用,类卸载就会失败
objs.add(o);
loaders.add(classLoader);
classes.add(clazz);
*/
//手动触发一次垃圾回收,但不是立即
//是对JVM的请求,具体是否回收JVM自行决断
System.gc();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Demo1 {
static{
System.out.println("类A被加载");
}
}
添加JVM参数:
//运行过程中打印类的加载信息
-XX:+TraceClassLoading
//类被卸载的时候打印一句日志
-XX:+TraceClassUnloading
运行:
但其实类的卸载不常见,因为自定义类的累加载器对象还在,热部署中用的多:
Java对象能否被回收,得看它是否还在被引用。
demo = null后,再无对Demo对象的引用,可回收。
执行:
a1 = null
b1 = null
A、B对象被回收,因为方法中无法再通过引用访问到这两个对象了。总之,判断方式:
即为每个对象维护一个计数器,对象被引用就+1,置为null了就-1,JVM扫描堆内存,发现数值为0则回收
A a1 = new A();
B b1 = new B():
a1.SetB(b1);
则A的计数器为1,B的计数器为2
优点:
缺点:
普通对象A,经一个引用链可以到达GC Root对象,则A不可被回收
GC Root对象包括:
步骤:
heapdump d:/tmp/test.hprof