直接内存、死锁、方法句柄

时间:2024-10-28 19:40:08

直接内存

1. 不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域
2. 直接内存是在Java堆外、直接向系统申请的内存区间
3. 来源于NIO,通过存在堆中的DirectByteBuffer操作Native内存
4. 通常,访问直接内存的速度会优于Java堆,即读写性能高
5. 处于性能考虑,读写操作频繁的场合会考虑使用直接内存
6. Java的NIO库允许Java程序使用直接内存,用于数据缓冲区
7. 由于直接内存在Java堆外,因此它的大小不会直接受限于-Xmx指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。
8. 直接内存的大小可以通过MaxDirectMemorySize设置,如果不指定,则默认于堆的最大值-Xmx参数值一致

 缺点:

  分配回收成本较高

  不受JVM内存回收管理

public class BufferTest {
    private static final int BUFFER=1024*1024*1024;//1GB

    public static void main(String[] args) {
//        直接分配内存空间
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
        System.out.println("直接内存分配完毕,请求指示!");

        Scanner scanner=new Scanner(System.in);
        scanner.next();

        System.out.println("直接内存开始释放");
        byteBuffer =null;
        System.gc();
        scanner.next();
    }
}

死锁

public class TestDeadLock implements Runnable {    
    public int flag = 1;    
    static Object o1 = new Object(), o2 = new Object();
    public void run() {    
    System.out.println("flag=" + flag);  
  
    if (flag == 1) {    
        synchronized (o1) {    
            try {    
                Thread.sleep(500);    
            } catch (Exception e) {    
                e.printStackTrace();    
            }    
            synchronized (o2) {    
                System.out.println("1");  
            }    
        }    
    }    
  
    if (flag == 0) {    
        synchronized (o2) {    
            try {    
                Thread.sleep(500);    
            } catch (Exception e) {    
                e.printStackTrace();    
            }    
            synchronized (o1) {    
                System.out.println("0");  
            }    
        }    
    }    
}

当flag为1时,线程首选获取o1锁,休眠500毫秒,然后尝试获取o2的锁,如果成功过,则打印"1".

当flag为2时,线程首选获取o2锁,休眠500毫秒,然后尝试获取o1的锁,如果成功过,则打印"0".

由于td1和flag为1,它首先获取了o1的锁,并休眠500毫秒。在td1休眠期间,td2开始执行并获取了o2的锁。当td1醒来兵尝试获取o2的锁时,它会被阻塞,因为o2的锁已经被td2持有,同样地,当td2醒来兵尝试获取o1的锁时,因为o1的锁已经被td1持有,这样,两个线程都陷入了等待状态,形成了死锁

为了避免死锁,可以采取以下策略:

方法句柄

 静态方法句柄
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleExample {

    public static void staticMethod(){
        System.out.println("static method called");
    }
    public static void main(String[] args) throws Throwable {
//        获取当前类的查找器
        MethodHandles.Lookup lookup=MethodHandles.lookup();
//        创建静态方法句柄
        MethodHandle methodHandle=lookup.findStatic(MethodHandleExample.class,"staticMethod", MethodType.methodType(void.class));
//        调用方法句柄
        methodHandle.invoke(); // static method called

    }
}
实例方法句柄
import java.lang.invoke.MethodHandle;  
import java.lang.invoke.MethodHandles;  
import java.lang.invoke.MethodType;  
  
public class MethodHandleExample {  
    public void instanceMethod() {  
        System.out.println("Instance method called");  
    }  
  
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException {  
        // 创建类的实例  
        MethodHandleExample example = new MethodHandleExample();  
          
        // 获取当前类的查找器  
        MethodHandles.Lookup lookup = MethodHandles.lookup();  
          
        // 创建实例方法句柄  
        MethodHandle methodHandle = lookup.findVirtual(MethodHandleExample.class, "instanceMethod", MethodType.methodType(void.class));  
          
        // 绑定实例并调用方法句柄  
        methodHandle.bindTo(example).invoke();  
    }  
}

构造函数句柄
import java.lang.invoke.MethodHandle;  
import java.lang.invoke.MethodHandles;  
import java.lang.invoke.MethodType;  
  
public class MethodHandleExample {  
    public MethodHandleExample() {  
        System.out.println("Constructor called");  
    }  
  
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException {  
        // 获取当前类的查找器  
        MethodHandles.Lookup lookup = MethodHandles.lookup();  
          
        // 创建构造函数句柄  
        MethodHandle constructorHandle = lookup.findConstructor(MethodHandleExample.class, MethodType.methodType(void.class));  
          
        // 调用构造函数句柄创建实例  
        MethodHandleExample example = (MethodHandleExample) constructorHandle.invoke();  
    }  
}
方法句柄优势

方法句柄比传统的Java反射API更快,因为他们直接利用了JVM内部机制

方法句柄可以绑定到各种类型的方法上,包括私有方法和构造函数

方法句柄的访问控制更加细粒度,可以在运行时动态调整权限

public class Test {
    class GrandFather{
        void thinking(){
            System.out.println("GrandFather ");
        }
    }
    class Father extends GrandFather{
        void thinking(){
            System.out.println("Father");
        }
    }
    class Son extends Father{
        void thinking(){

            try {
                MethodType mt=MethodType.methodType(void.class);
                
//            获取当前类的查找器
                MethodHandles.Lookup lookup=MethodHandles.lookup();
//            创建句柄
                MethodHandle mh=lookup.findSpecial(GrandFather.class,"thinking",mt,getClass());

                mh.invoke(this);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }

        }
    }

    public static void main(String[] args) {
        (new Test().new Son()).thinking();
    }
}
public class Test {
    class GrandFather{
        void thinking(){
            System.out.println("GrandFather ");
        }
    }
    class Father extends GrandFather{
        void thinking(){
            System.out.println("Father");
        }
    }
    class Son extends Father{
        void thinking(){

            try {
                MethodType mt=MethodType.methodType(void.class);
                // 反射,绕过保护机制
                Field lookupImpl =MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
                
                lookupImpl.setAccessible(true);
//            获取当前类的查找器
//                MethodHandles.Lookup lookup=MethodHandles.lookup();
//            创建句柄
                MethodHandle mh=((MethodHandles.Lookup)lookupImpl.get(null)).findSpecial(GrandFather.class,"thinking",mt,GrandFather.class);

                mh.invoke(this);  //GrandFather
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }

        }
    }

    public static void main(String[] args) {
        (new Test().new Son()).thinking();
    }
}