JAVA 内存泄漏相关知识学习

1 强引用

强引用回收机制
强引用需要切断引用关系,才可以让对象被垃圾回收器正常回收

示例:

 @Test
    fun testMemoryLeak(){
        var m:M? = M()
        System.gc()
        println("打印m对象 == $m")
        m = null
        System.gc()
        println("置空之后打印m == $m")
    }

结果:

打印m对象 == com.example.testdemo2.M@6f53b8a
置空之后打印m == null
该对象被回收了finalize

2 弱引用

弱引用的回收机制
弱引用只要java触发gc垃圾回收,被弱引用持有的对象就会被回收掉。

示例:

 @Test
    fun testWeakReference(){
        val ref = WeakReference<Person>(Person())
        println("打印弱引用持有的对象 == ${ref.get()}")
        Thread.sleep(5000)
        println("打印弱引用持有的对象 == ${ref.get()}")
        Thread.sleep(5000)
        System.gc()
        println("GC之后打印弱引用持有的对象 == ${ref.get()}")
    }

    class Person{
        var name = "zhangsan"
    }

结果:

打印弱引用持有的对象 == com.example.testdemo2.ExampleUnitTest$Person@6b695b06

打印弱引用持有的对象 == com.example.testdemo2.ExampleUnitTest$Person@6b695b06
GC之后打印弱引用持有的对象 == null

3 软引用

软引用回收机制
软引用会在jvm分配内存不足的时候,释放回收其引用的对象,在触发gc时,只要内存分配足够,是不会被回收的,只有到使用内存大于分配内存的时候才会回收该对象的引用。

示例

 @Test
    fun testSoftReference() {
        val softRef = SoftReference<ByteArray>(ByteArray(1024 * 1024 * 10))
        println("打印软引用 == ${softRef.get()}")
        System.gc()
        Thread.sleep(500)
        println("打印软引用 == ${softRef.get()}")
        val softRef2 = SoftReference<ByteArray>(ByteArray(1024 * 1024 * 12))
        println("打印软引用 == ${softRef.get()}")
    }

结果:
jvm分配空间为20M的情况下,就会造成软引用中的对象被回收掉

打印软引用 == [B@58a9760d
打印软引用 == [B@58a9760d
打印软引用 == null

4 ThreadLocal的使用

ThreadLocal 自带线程隔离机制,因为ThreadLocal在设置对象的时候,会用当前的线程作为key存储当前设置的引用对象,所以每个线程中只能获取到自己存放的引用对象,无法获取其他线程中存储的值,从而起到的线程隔离的作用。

示例

 @Test
    fun testThreadLocal(){
        val threadLocal = ThreadLocal<Person>()
        Thread{
            threadLocal.set(Person())
            println("线程1 == 打印threadLocal中的存储的信息 == ${threadLocal.get()}")
            threadLocal.remove()
            println("线程11 == 打印threadLocal中的存储的信息 == ${threadLocal.get()}")
        }.start()

        Thread{
            println("线程2 == 打印threadLocal中的存储的信息 == ${threadLocal.get()}")
        }.start()
    }

    class Person{
        var name = "zhangsan"
    }

结果

线程2 == 打印threadLocal中的存储的信息 == null
线程1 == 打印threadLocal中的存储的信息 == com.benben.TestDataDemo.TestDataDemoApplicationTests$Person@6388e817
线程11 == 打印threadLocal中的存储的信息 == null

从结果上看 只有线程1的日志中正常拿到了设置进去的Person对象,线程11之所以没有拿到 是因为当前线程中的threadLocal对象已经将存储的信息进行了remove,线程2拿不到的原因是因为不在同一个线程中,即使使用同一个threadLocal对象也无法获取相同的map容器,所以获取存储对象的时候拿到了null

5 集合容器内存泄漏示例

 @Test
    fun testListLeak() {
        var list: MutableList<M?>? = mutableListOf<M?>()
        for (i in 0 until 10) {
            var m: M? = M(i)
            list?.add(m)
            m = null
        }
        System.gc()
        println("当前容器中的数量 = ${list?.size}")
    }

运行结果:

当前容器中的数量 = 10

结论:
主动调用了System.gc() 启动垃圾回收,手动给集合中的每个m引用都置为空,这是观察集合中m对象的释放情况,并没有看到m对象被回收的日志,原因是我们虽然置空了每个m对象的引用,但是对象的示例还是存在集合当中,所以垃圾回收期回收不了当前的10个m对象。

解决方案

  @Test
    fun testListLeakSuccess() {
        var list: MutableList<M?>? = mutableListOf<M?>()
        for (i in 0 until 10) {
            var m: M? = M(i)
            list?.add(m)
            m = null
        }
        list?.clear()
        list = null
        System.gc()
        println("当前容器中的数量 = ${list?.size}")
    }

运行结果

当前容器中的数量 = null
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize
该对象被回收了finalize

在集合容器List不用的时候,可以清理掉内部的内容,并且切断list对象和示例的直接引用关系(设置list = null),
这样在垃圾回收器启动回收的时候,发现该集合属于垃圾,就可以正常的进行回收了,并且可以正常把内部的M也同时清理掉。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值