Java核心基础之方法引用
近期在研究SpringMVC和MybatisPlus组合项目,对于国内java架构常用所有表添加统一字段的统一处理场景,采用AOP拦截器如何在无Request对象时获得session数据中,采用ThreadLocal
内存本地对象解决方案,忽然意识到高并发场景极有可能发生内存泄漏,因此有了本篇文章。
说到内存泄漏,这里不得不提Java的内存回收,GC算法。
关于GC
GC也就是JVM Garbage Collectors的缩写,做过c/cpp的小伙伴都知道内存管理是很麻烦的,所以本章我们将简单介绍下jvm的内存结构,GC的原理和算法!这里参考资料来源:一文看懂JVM内存布局及GC原理_技术管理_杨俊明_InfoQ精选文章
JVM运行时内存布局
来自Java8的[虚拟机规范](Chapter 2. The Structure of the Java Virtual Machine (oracle.com))用语Run-Time Data Areas
GC内存回收主要战场,集中在上图三个区域
- Heap:GC 垃圾回收的主站场,存放类的实例对象及 Arrays 实例等
- Method Area:方法区,存放类结构、类成员定义,static 静态成员等。
- Runtime Constant Pool:运行时常量池,比如:字符串,int -128~127 范围的值等
Heap、Method Area 都是在虚拟机启动时创建,虚拟机退出时释放。
GC垃圾回收原理
垃圾判断算法
主要有引用计数
和可达性分析
两种,引用计数法虽然简单易用,但是碰到交叉引用就不灵了,目前主流JVM都采用可达性分析为主!
那么问题来了,那些内存需要回收?
垃圾回收算法
常见四种算法,都不完美,最后一种复杂但使用,类似磁盘碎片整理,详细来看看
1)、mark-sweep 标记清除法
从图上可知,对于垃圾内存进行了标记删除,产生了很多碎片!
2)、mark-copy 标记复制法
简单粗暴的55分法,对于内存充裕,标记完快速搬迁,效果很明显,缺点也同样明显,那就是内存要充裕
,但现实往往是内存最紧张的时候来进行GC啊。
3)、mark-compact 标记-整理(也称标记-压缩)法
标记整理综合上面两种算法的优缺点,唯一不足的是效率不高
4)、generation-collect 分代收集算法
上述三种算法,每种都有各自的优缺点,都不完美。现代 JVM 中,往往是综合使用的,经过大量实际分析发现大部分对象其实相当短命,很少有对象能在 GC 后活下来。因此诞生了分代的思想,以 Hotspot 为例(JDK 7):
内存分成了三大块:年青代(Young Genaration),老年代(Old Generation),永久代(Permanent Generation),其中 Young Genaration 更是又细为分 eden,S0,S1 三个区。
结合我们经常使用的一些 jvm 调优参数后,一些参数能影响的各区域内存大小值,具体暂时不展开,下面我们来深究下垃圾判断算法的四种引用类型。
Java四大引用类型
java的内存分配和回收都交给JVM统一处理啦,回收的基础是提前做了引用区分,给可达性分析提供了基础!
根据优先级高低分别为:强引用、软引用、弱引用、虚引用。
强引用
强引用是非常普遍的引用类型,我们常见、常写的代码几乎都是,如下:
Object o = new Object;
强引用分配出去的内存,哪怕是栈溢出、堆溢出都不会被GC进程回收,除非做了这个操作 o = null;
这里我们做个实验
public class Student {
@Override
protected void finalize() throws Throwable {
System.out.println("Student 被回收了");
}
}
public static void main(String[] args) {
Student student = new Student();
student = null;
System.gc();
}
重写finalize方法仅用于实验,实际场景不适合。
强引用特点:jvm宁可内存溢出也不回收,除非显式置为NULL!
软引用
软引用特点:GC后也不强制回收,除非内存真不足。
实验:
SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024*1024*10]);
System.out.println(softReference.get());
System.gc();
System.out.println(softReference.get());
byte[] bytes = new byte[1024 * 1024 * 5];
System.out.println(softReference.get());
执行结果:
E:\Java\jdk1.8.0_121\bin\java.exe -Xmx20M -javaagent:C:\java\IntelliJIDEA2022.1.3\lib\idea_rt.jar=4771:C:\Java\IntelliJIDEA2022.1.3\bin -Dfile.encoding=UTF-8 -classpath
[B@4783da3f
[B@4783da3f
null
这里可以提前在idea的运行配置设定内存为20M,当内存使用超过75%时即可触发!
弱引用
弱引用特点:GC后立刻回收
实验:
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1]);
System.out.println(weakReference.get());
System.gc();
System.out.println(weakReference.get());
执行结果:
[B@4783da3f
null
虚引用
虚引用特点:强制标记回收
实验:
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue);
System.out.println(reference.get());
执行结果:
null
分析:
get方法直接返回null,且虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。应用queue提交回收通知
,应用场景比较特殊,主要用于NIO时堆外内存的管理!
絮叨
JVM的内存管理技术让很大程度上让java的发展远远超过c/CPP,了解jvm内存结构、gc算法、四大引用类型绝对能让编程事业锦上添花!
评论系统未开启,无法评论!