JAVA随笔

tech2024-12-15  13

1、内存泄漏的一点思考(JAVA)

参考:http://blog.csdn.net/anxpp/article/details/51325838

一句话:命长的拉着命短的不放手(引用),导致命短的不能死去(gc)。

我们举一个简单的例子:

public class Simple { Object object; public void method1(){ object = new Object(); //...其他代码 } }

这里的object实例,其实我们期望它只作用于method1()方法中,且其他地方不会再用到它,但是,当method1()方法执行完成后,object对象所分配的内存不会马上被认为是可以被释放的对象,只有在Simple类创建的对象被释放后才会被释放,严格的说,这就是一种内存泄露。

解决方法就是将object作为method1()方法中的局部变量。当然,如果一定要这么写,可以改为这样:

public class Simple { Object object; public void method1(){ object = new Object(); //...其他代码 object = null; } }

网上很多博客都会谈到TreadLocal存在内存泄漏问题,有丰富理论和代码论证,比如如下博客:

该博客的分析是没有问题的,的确线程池中的线程持有Entry数组中的Entry对Value持有强引用,因此即使ThreadLocal对象因为作用域不存在而被gc,但是int[]数组不会被gc。

但是谁会在线程中不断的new ThreadLocal对象呢?

ThreadLocal对象一般用于存储线程上下文,一般都会定义成static的。

有人认为TreadLocal对象只执行set而从不remove就会造成内存泄漏,比如:

ThreadLocal出现OOM内存溢出的场景和原理分析

我认为这篇博客是错误的,因为这不存在命长的对象持有短名鬼,其中ThreadLocal对象是static的,其生命周期和线程都和程序生命周期一致,每次执行set方法,key都是那个key,value引用才是不断变化的,但旧的对象会因为没有value引用而可以被GC呀!上面这篇文章的现象,仅仅是内存溢出,而不是内存泄漏!

2、synchronized优化

在 Java 5 之前,synchronized依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作。

现代的(Oracle)JDK 中,JVM 对此进行了大刀阔斧地改进,提供了三种不同的 Monitor 实现,也就是常说的三种不同的锁:偏斜锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。

当没有竞争出现时,默认会使用偏斜锁。JVM 会利用 CAS 操作(compare and swap),在对象中记录线程 ID,以表示这个对象偏向于当前线程,所以并不涉及真正的互斥锁。这样做的假设是基于在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏斜锁可以降低无竞争开销。

比如web应用的转账操作,账户A给账户B转账,为了防止同时存在涉及账户A和B的转账操作,必须对账户A和账户B加锁,但是试问又有多大可能存在上述并发呢?但是1.5以前也只能使用synchronized来同步,所以每次都切换到内核态都是无用功!

锁的4中状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态(级别从低到高)

(1)偏向锁:

为什么要引入偏向锁?

因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。

偏向锁的升级

当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

(2)轻量级锁

为什么要引入轻量级锁?

轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。

轻量级锁什么时候升级为重量级锁?

线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。

但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。

3.mybatis的collection+select性能太差

会串行的执行子查询,导致执行SQL非常多 采用collection+resultmap+join一次性查询性能可能会好很多。

4. MySQL

group by a,b,c order by null 通过order by null 避免group by 的排序,提高性能

最新回复(0)