本身java有垃圾回收器GC,可以内存管理,但为什么还会造成内存泄漏(内存泄漏不等于内存溢出),内存泄漏在项目实战或者企业项目是不被允许,甚至在企业面试中也是常考的题型
了解什么是内存泄漏,需要知道具体的定义、检测以及解决方式
内存泄漏:对系统申请内存使用,将其内存分配给对象使用,但内存空间使用完毕后未释放,一直占用内存空间。(长期的堆积,内存迟早被耗尽,所以今早的解决内存泄漏无疑是好事)
本身内存泄漏就是缺点,没有所谓的优点
缺点如下:
以下的方式会造成内存泄漏的风险,所以谨慎使用
回顾下static的知识点:java零基础从入门到精通(全)
类似的伪代码如下:
public class test{
// 静态变量
public void method() {
// 该函数中使用了静态变量
}
public static void main(String[] args) {
// 业务逻辑代码
// 此处调用了method来操作静态变量
// 业务逻辑代码
}
}
如上就会造成一时的内存泄漏,直到程序运行完毕才会释放
这是因为静态变量的生命周期和主函数保持一致,所以尽量减少使用静态变量
(补充一下常考的面试题)
这个方法常和final、finally与finalize作比较
以下是finalize的整体流程:
之所以会造成内存泄漏,是因为在垃圾回收的时候,如果重写了finalize方法而且该对象的finalize方法没有被执行过,但是进入队列之后一直没被调用就会一直占用内存空间
业务逻辑代码模块中,稍微不注意就可能引发内存泄漏,一句代码也可能引发致命错误
具体如下:
int[] a = new int[5];
int[] b = new int[5];
b = a;
原本GC清除的是引用地址,此处这样调用,b的引用地址就会变成了a,导致b的引用地址就会找不到,GC内存回收的时候就会造成内存泄漏。(尽量避免这种情况的发生)
在使用到jdbc、数据库连接、io连接等,都会进行一个jvm的分配内存
最后代码模块都会进行一个资源的关闭,如果资源未被正常关闭,内存就一直在运行,直到主函数关闭(但是并不是时时刻刻都在连接查询资源)
补充:对应的知识可看我之前的文章:
补充以下ThreadLocal的底层原理以及知识点:
每个线程都有自个独立的ThreadLocalMap对象(Entry对象)
如果当前线程对应的ThreadLocalMap对象为空,则为其创建key以及value(key为ThreadLocal对象,value为缓存变量值)
Threadlocal<String> stringThreadlocal=new Threadlocal<>();
stringThreadlocal.set("码农研究僧");
ThreadLocalMap可以存放多个ThreadLocal对象
每个ThreadLocal对象只能缓存一个变量值
通过ThreadLocal.get()可以获取相对应的key值
ThreadLocalMap 的entry对象为,key为ThreadLocal,value为缓存变量值
主线程调用了ThreadLocal,给予其变量为null(引用断开了,而不是单纯没有引用)。但是当前线程的ThreadLocalMap还是会引用其堆的变量,如果是强引用,gc是不会回收的。
代码如下所示:(该变量不会被gc回收)
ThreadLocal<String>ss =new ThreadLocal<>();
ss.set("码农研究僧");
ss=null
如果修改其变量引用指向,才会被gc回收
ThreadLocalMap删除Entry的对象,才会解决其引用断开,所以才会被gc清理掉
代码如下所示:
ThreadLocal<String>ss =new ThreadLocal<>();
ss.set("码农研究僧");
//ThreadLocalMap与堆内存中的ThreadLocal断开引用
ss.remove();
//ss与堆内存中的ThreadLocal断开引用
ss=null
GCroot引用链就会发现ThreadLocal没有被任何人引用就会被清理掉该对象,避免内存泄漏问题
关于避免ThreadLocal内存泄漏问题
可以通过:
如果在实际应用场景中遇到一些内存泄漏的问题
可在底下评论区留言
如果不使用工具来检测的话,可通过JVM自带的一些命令参数:
也可通过IDEA配置-verbose:gc
JVM的检测巩固可通过java的VisualVM、YourKit、Netbeans Profiler等
通过查看堆内存,打印堆的各个使用情况
或者将其Dump文件分析
对应的检测工具跟命令大同小异,都是分析其对应内存是否有泄漏情况