设为首页 加入收藏

TOP

Java不能操作内存?Unsafe了解一下(一)
2023-08-26 21:11:18 】 浏览:96
Tags:Java 能操作 Unsafe 解一下

前言

C++可以动态的分类内存(但是得主动释放内存,避免内存泄漏),而java并不能这样,java的内存分配和垃圾回收统一由JVM管理,是不是java就不能操作内存呢?当然有其他办法可以操作内存,接下来有请Unsafe出场,我们一起看看Unsafe是如何花式操作内存的。

Unsafe介绍

Unsafe见名知意,不安全的意思,因为通过这个类可以绕过JVM的管理直接去操作内存,如果操作不当或者使用完成后没有及时释放的话,这部分的内存不会被回收,久而久之,这种没有被释放的内存会越来越多造成内存泄漏。所以这是一个比较不安全的操作,一般不建议直接使用,毕竟这种问题导致的线上问题很难查出,另外通常的解决办法就是重启系统了。
虽然Unsafe是不安全的操作,但可以根据实际情况合理使用可以达到更好的效果,比如像java NIO里就有通过这种方式创建堆外存。Unsafe常用的操作包括:分配内存释放内存、实例化及对象操作、数组操作、CAS、线程同步等。

Unsafe实例对象的获取

Unsafe不能直接获取到,像下面这样使用会直接抛出安全检查异常。

public static void main(String[] args) throws Exception {
    Unsafe unsafe = Unsafe.getUnsafe();
}

运行结果就这样:

Exception in thread "main" java.lang.SecurityException: Unsafe
	at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
	at com.star95.study.UnsafeTest.main(UnsafeTest.java:21)

我们来看看sun.misc.Unsafe.getUnsafe这个方法的源码:

public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}

这里会判断这个类的加载器是否是启用类加载器Bootstrap ClassLoader,如果不是启动类加载器加载的则抛异常。
Bootstrap ClassLoader这个类加载器主要加载java核心类库,比如rt.jar这类包的,java采用的是双亲委托方式加载类,如果父加载器已加载了某个类,则子加载器不再加载,采用这样的方式进一步加强安全性。关于启动类加载器Bootstrap ClassLoader、扩展类加载器Extension Classloader、应用程序类加载器Application Classloader这里不再介绍,感兴趣的可自行搜索。
以下介绍3种方法来获取Unsafe

1、修改启动类加载器的搜索路径

public class UnsafeUtil {
    private UnsafeUtil() {}
    public static Unsafe getUnsafe() {
        System.out.println("get getUnsafe...");
        return Unsafe.getUnsafe();
    }
}

就这一个类,打成一个jar包。

把这个jar包的路径放到jvm启动参数里-Xbootclasspath/a:D:\work\projects\test\unsafe\target\unsafe-1.0-SNAPSHOT.jar,然后调用即可获取到Unsafe

关于jvm参数-Xbootclasspath说明:

-Xbootclasspath:新jar路径(Windows用;分隔,linux用:分隔),这种相当于覆盖了java默认的核心类搜索路径,包括核心类库例如rt.jar,这种基本不用。

-Xbootclasspath/a:新jar路径(Windows用;分隔,linux用:分隔),这种是把新jar路径追加到已有的classpath后,相当于扩大了核心类的范围,这个常用。

-Xbootclasspath/p:新jar路径(Windows用;分隔,linux用:分隔),这种是把新jar路径追加到已有的classpath前,也相当于扩大了核心类的范围,但是放到核心类前可能会引起冲突,这个不常用。

2、利用反射使用构造方法创建实例

Unsafe有一个私有的无参构造方法,利用反射使用构造方法也可以创建Unsafe实例。

Constructor constructor = Unsafe.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Unsafe unsafe = (Unsafe)constructor.newInstance();
System.out.println(unsafe);

3、利用反射获取Unsafe属性创建实例

Unsafe类里有一个属性private static final Unsafe theUnsafe;利用反射获取到这个属性然后对null这个对象获取属性值就会触发类静态块儿的执行,从而达到实例化的目的。

private static Unsafe getUnsafe() {
    try {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe)field.get(null);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

以上3种方法虽然都可以获取到Unsafe的实例,但第三种更常用一些。

Unsafe常用操作

Unsafe类里大概有100多个方法,按用途主要分为以下几大类,分别介绍。

Unsafe操作内存

内存操作主要包括内存分配、扩展内存、设置内存值、释放内存等,常用的方法介绍如下。

//分配指定大小的内存
public long allocateMemory(long bytes)
//根据给定的内存地址address调整内存大小
public long reallocateMemory(long address, long bytes)
//设置内存值
public void setMemory(Object o, long offset, long bytes, byte value)
public void setMemory(long address, long bytes, byte value)
//内存复制,支持两种地址模式
public void copyMemory(Object srcBase, long srcOffset,  Object destBase, long destOffset, long bytes)
//释放allocateMemory和reallocateMemory申请的内存
pu
首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇10、Spring之AOP概述 下一篇new ArrayList 不当导致 CPU 飙升..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目