1.java如何手工管理内存
- Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域,不能像C++中那样可以自己申请内存和释放内存。Java中的Unsafe类为我们提供了类似C++手动管理内存的能力。
- Unsafe类,全限定名是
sun.misc.Unsafe
,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。
2.如何获取Unsafe实例
- Unsafe类是"final"的,不允许继承。且构造函数是private的:
public final class Unsafe {
private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
private static native void registerNatives();
// 构造函数是private的,不允许外部实例化
private Unsafe() {
}
...
}
因此我们无法在外部对Unsafe进行实例化。
- 获取Unsafe
Unsafe无法实例化,那么怎么获取Unsafe呢?答案就是通过反射来获取Unsafe:
public Unsafe getUnsafe() throws IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
return unsafe;
}
3. Unsafe是什么?
- Java 无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM 还是开了一个后门,JDK 中有一个类 Unsafe,底层是使用C/C++写的,它提供了硬件级别的原子操作。Unsafe为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。
- UnSafe的功能主要有:(1) 实例化一个类;(2) 修改私有字段的值;(3) 抛出checked异常;(4) 使用堆外内存;(5) CAS操作;(6) 阻塞/唤醒线程;(7) 内存屏障
4. Unsafe中的CAS操作
- JUC中用到了大量的CAS,他们的底层其实都是采用Unsafe的CAS操作
- CAS(比较与交换,Compare and swap)是一种有名的无锁算法,因为不需要加锁,性能比加锁高。CAS是一个CPU指令,CAS还是一个乐观锁技术
- CAS存在的问题:
i. 经典的ABA问题,危害有(以栈举例),解决方案:版本号控制,有的数据结构在高位用邮戳标记;不重复使用节点引用,而是构建新的节点
ii. CAS常常搭配自旋一起使用,如果自选长时间不成功,循环时间长 开销大
iii. 只能保持一个共享变量的安全操作
5. Unsafe的阻塞/唤醒操作
LockSupport类中的park与unpark方法对unsafe中的park与unpark方法做了封装,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法
6. 实例化类的六种方式
a. 通过构造方法new一个对象
b. 通过Class实例一个类,Class.forName("包名.类名").newInstance()
c. 通过反射实例化一个类,class.newInstance()
d. 通过克隆,需实现Cloneable接口,或覆盖Object中的clone()方法
e. 通过反序列化
f. 通过Unsafe实例化一个类,unsafe.allocateInstance(xxx.class)
7.例子
(1)Unsafe CAS
public class UnsafeCounter {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(1000);
//CASCounter result: 1000000, Timer passed ms: 137
//AtomicCounter result: 1000000. Timer passed ms: 153
//LockCounter result: 1000000. Timer passed ms: 175
//SynCounter result: 1000000. Timer passed ms: 159
//StupidCounter result: 946790. Timer passed ms: 158
Counter counter = new StupidCounter();
long start=System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
service.execute(new CounterRunnable(counter, 1000));
}
service.shutdown();
service.awaitTermination(1, TimeUnit.HOURS);
long end = System.currentTimeMillis();
System.out.println("Counter result: " + counter.getCounter() + ". Timer passed ms: " + (end - start));
}
public static Unsafe getUnsafe(){
try {
Field unsafe = Unsafe.class.getDeclaredField("theUnsafe");
unsafe.setAccessible(true);
return (Unsafe)unsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("get unsafe object failed");
}
}
interface Counter{
void increment();
long getCounter();
}
static class StupidCounter implements Counter{
private volatile long counter = 0;
@Override
public void increment() {
counter++;
}
@Override
public long getCounter() {
return counter;
}
}
static class SynCounter implements Counter{
private long counter = 0;
@Override
public synchronized void increment() {
counter++;
}
@Override
public long getCounter() {
return counter;
}
}
static class LockCounter implements Counter{
private long counter = 0;
private final Lock lock = new ReentrantLock();
@Override
public void increment() {
try{
lock.lock();
counter++;
}finally {
lock.unlock();
}
}
@Override
public long getCounter() {
return counter;
}
}
static class AtomicCounter implements Counter{
private AtomicLong counter = new AtomicLong();
@Override
public void increment() {
counter.getAndIncrement();
}
@Override
public long getCounter() {
return counter.get();
}
}
static class CASCounter implements Counter{
private volatile long counter = 0;
private Unsafe unsafe;
private long offset;
CASCounter() throws NoSuchFieldException {
unsafe = getUnsafe();
offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
}
@Override
public void increment() {
long current = counter;
while (!unsafe.compareAndSwapLong(this, offset, current, current+1)){
current = counter;
}
}
@Override
public long getCounter() {
return counter;
}
}
static class CounterRunnable implements Runnable{
private final Counter counter;
private final int num;
public CounterRunnable(Counter counter, int num) {
this.counter = counter;
this.num = num;
}
@Override
public void run() {
for (int i = 0; i < this.num; i++) {
counter.increment();
}
}
}
}