一篇看懂Java中的Unsafe类
作者:Ye_yang 发布时间:2022-02-13 04:02:38
前言
本文主要给大家介绍了关于Java中Unsafe类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧
1.Unsafe类介绍
Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty、Hadoop、Kafka等。
使用Unsafe可用来直接访问系统内存资源并进行自主管理,Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。
Unsafe可认为是Java中留下的后门,提供了一些低层次操作,如直接内存访问、线程调度等。
官方并不建议使用Unsafe。
下面是使用Unsafe的一些例子。
1.1实例化私有类
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class UnsafePlayer {
public static void main(String[] args) throws Exception {
//通过反射实例化Unsafe
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
//实例化Player
Player player = (Player) unsafe.allocateInstance(Player.class);
player.setName("li lei");
System.out.println(player.getName());
}
}
class Player{
private String name;
private Player(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.2CAS操作,通过内存偏移地址修改变量值
java并发包中的SynchronousQueue中的TransferStack中使用CAS更新栈顶。
/ Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = TransferStack.class;
headOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("head"));
} catch (Exception e) {
throw new Error(e);
}
}
//栈顶
volatile SNode head;
//更新栈顶
boolean casHead(SNode h, SNode nh) {
return h == head &&
UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
}
1.3直接内存访问
Unsafe的直接内存访问:用Unsafe开辟的内存空间不占用Heap空间,当然也不具有自动内存回收功能。做到像C一样自由利用系统内存资源。
2.Unsafe类源码分析
Unsafe的大部分API都是native的方法,主要包括以下几类:
1)Class相关。主要提供Class和它的静态字段的操作方法。
2)Object相关。主要提供Object和它的字段的操作方法。
3)Arrray相关。主要提供数组及其中元素的操作方法。
4)并发相关。主要提供低级别同步原语,如CAS、线程调度、volatile、内存屏障等。
5)Memory相关。提供了直接内存访问方法(绕过Java堆直接操作本地内存),可做到像C一样自由利用系统内存资源。
6)系统相关。主要返回某些低级别的内存信息,如地址大小、内存页大小。
2.1Class相关
//静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类
public native boolean shouldBeInitialized(Class<?> c);
//确保类被初始化
public native void ensureClassInitialized(Class<?> c);
//定义一个类,可用于动态创建类
public native Class<?> defineClass(String name, byte[] b, int off, int len,
ClassLoader loader,
ProtectionDomain protectionDomain);
//定义一个匿名类,可用于动态创建类
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
2.2Object相关
Java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。
//获得对象的字段偏移量
public native long objectFieldOffset(Field f);
//获得给定对象地址偏移量的int值
public native int getInt(Object o, long offset);
//设置给定对象地址偏移量的int值
public native void putInt(Object o, long offset, int x);
//创建对象,但并不会调用其构造方法。如果类未被初始化,将初始化类。
public native Object allocateInstance(Class<?> cls)
throws InstantiationException;
2.3数组相关
/**
* Report the offset of the first element in the storage allocation of a
* given array class. If {@link #arrayIndexScale} returns a non-zero value
* for the same class, you may use that scale factor, together with this
* base offset, to form new offsets to access elements of arrays of the
* given class.
*
* @see #getInt(Object, long)
* @see #putInt(Object, long, int)
*/
//返回数组中第一个元素的偏移地址
public native int arrayBaseOffset(Class<?> arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/** The value of {@code arrayBaseOffset(boolean[].class)} */
public static final int ARRAY_BOOLEAN_BASE_OFFSET
= theUnsafe.arrayBaseOffset(boolean[].class);
/**
* Report the scale factor for addressing elements in the storage
* allocation of a given array class. However, arrays of "narrow" types
* will generally not work properly with accessors like {@link
* #getByte(Object, int)}, so the scale factor for such classes is reported
* as zero.
*
* @see #arrayBaseOffset
* @see #getInt(Object, long)
* @see #putInt(Object, long, int)
*/
//返回数组中每一个元素占用的大小
public native int arrayIndexScale(Class<?> arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/** The value of {@code arrayIndexScale(boolean[].class)} */
public static final int ARRAY_BOOLEAN_INDEX_SCALE
= theUnsafe.arrayIndexScale(boolean[].class);
通过arrayBaseOffset和arrayIndexScale可定位数组中每个元素在内存中的位置。
2.4并发相关
2.4.1CAS相关
CAS:CompareAndSwap,内存偏移地址offset,预期值expected,新值x。如果变量在当前时刻的值和预期值expected相等,尝试将变量的值更新为x。如果更新成功,返回true;否则,返回false。
//更新变量值为x,如果当前值为expected
//o:对象 offset:偏移量 expected:期望值 x:新值
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x);
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
public final native boolean compareAndSwapLong(Object o, long offset,
long expected,
long x);
从Java 8开始,Unsafe中提供了以下方法:
//增加
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
public final long getAndAddLong(Object o, long offset, long delta) {
long v;
do {
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, v + delta));
return v;
}
//设置
public final int getAndSetInt(Object o, long offset, int newValue) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, newValue));
return v;
}
public final long getAndSetLong(Object o, long offset, long newValue) {
long v;
do {
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, newValue));
return v;
}
public final Object getAndSetObject(Object o, long offset, Object newValue) {
Object v;
do {
v = getObjectVolatile(o, offset);
} while (!compareAndSwapObject(o, offset, v, newValue));
return v;
2.4.2线程调度相关
//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁
public native void monitorEnter(Object o);
//释放对象锁
public native void monitorExit(Object o);
//尝试获取对象锁,返回true或false表示是否获取成功
public native boolean tryMonitorEnter(Object o);
2.4.3volatile相关读写
Java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
//相当于getObject(Object, long)的volatile版本
public native Object getObjectVolatile(Object o, long offset);
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
//相当于putObject(Object, long, Object)的volatile版本
public native void putObjectVolatile(Object o, long offset, Object x);
/**
* Version of {@link #putObjectVolatile(Object, long, Object)}
* that does not guarantee immediate visibility of the store to
* other threads. This method is generally only useful if the
* underlying field is a Java volatile (or if an array cell, one
* that is otherwise only accessed using volatile accesses).
*/
public native void putOrderedObject(Object o, long offset, Object x);
/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */
public native void putOrderedInt(Object o, long offset, int x);
/** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
public native void putOrderedLong(Object o, long offset, long x);
2.4.4内存屏障相关
Java 8引入 ,用于定义内存屏障,避免代码重排序。
//内存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//内存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();
2.5直接内存访问(非堆内存)
allocateMemory所分配的内存需要手动free(不被GC回收)
//(boolean、byte、char、short、int、long、float、double)都有以下get、put两个方法。
//获得给定地址上的int值
public native int getInt(long address);
//设置给定地址上的int值
public native void putInt(long address, int x);
//获得本地指针
public native long getAddress(long address);
//存储本地指针到给定的内存地址
public native void putAddress(long address, long x);
//分配内存
public native long allocateMemory(long bytes);
//重新分配内存
public native long reallocateMemory(long address, long bytes);
//初始化内存内容
public native void setMemory(Object o, long offset, long bytes, byte value);
//初始化内存内容
public void setMemory(long address, long bytes, byte value) {
setMemory(null, address, bytes, value);
}
//内存内容拷贝
public native void copyMemory(Object srcBase, long srcOffset,
Object destBase, long destOffset,
long bytes);
//内存内容拷贝
public void copyMemory(long srcAddress, long destAddress, long bytes) {
copyMemory(null, srcAddress, null, destAddress, bytes);
}
//释放内存
public native void freeMemory(long address);
2.6系统相关
//返回指针的大小。返回值为4或8。
public native int addressSize();
/** The value of {@code addressSize()} */
public static final int ADDRESS_SIZE = theUnsafe.addressSize();
//内存页的大小。
public native int pageSize();
3.参考资料
https://www.jb51.net/article/140709.htm 说一说Java中的Unsafe类
https://www.jb51.net/article/140721.htm java魔法类:sun.misc.Unsafe
来源:https://www.cnblogs.com/yeyang/p/9074894.html


猜你喜欢
- 这篇文章主要介绍了SpringBoot整合Druid数据源过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价
- 本文实例讲述了WPF设置窗体可以使用鼠标拖动大小的方法。分享给大家供大家参考。具体实现方法如下:private void Window_Lo
- package cn.mypic; import java.io.Buffe
- 不用单点登录,模拟远程项目的登录页面表单,在访问这个页面的时候自动提交表单到此项目的登录action,就可以实现登录到其他系统。ssh框架项
- 项目结构项目路径可以自己定义,只要路径映射正确就可以pom.xml <properties> <spring.versio
- 要使用SAPI,首先添加引用DotNetSpeech,请自行下载DotNetSpeech.dll。 初始化对象,SpVoice voice
- 本文使用Matrix实现Android实现图片缩放与旋转。示例代码如下:package com.android.matrix;import
- 嵌入式Servlet容器在Spring Boot中,默认支持的web容器有 Tomcat, Jetty, 和 Undertow1、原理分析那
- java 根据经纬度获取地址实现代码实现代码:public class GetLocation { public
- android原生的Spinner提供了下拉列表选项框,但在一些流行的APP中,原生的Spinner似乎不太受待见,而通常会有下图所示的下拉
- 实现多文件的上传,基于标准的http来实现。1.多文件上传MyUploader类的实现:/** * * 同步上传多个文件 * 基于标准的h
- 本文实例为大家分享了spring aop注解配置的具体代码,供大家参考,具体内容如下Demo.javapackage cn.itcast.e
- 一.前言这一篇来看看 SpringIOC 里面的一个细节点 , 来简单看看 BeanDefinition 这个对象 , 以及有没有办法对其进
- 前言在消息发送过程中,生产者从NameServer中获取到了指定Topic对应的Broker信息,在同步发送消息的代码中,如果消息发送失败,
- 在开发中,经常会遇到键盘挡住输入框的情况,比如登录界面或注册界面,弹出的软键盘把登录或注册按钮挡住了,用户必须把软键盘收起,才能点击相应按钮
- 在讲使用path绘制多边形时,讲下Canvas的translate(),rotate()方法的使用,本博客中会使用这方面的知识,先单独讲下,
- 目录1. 前言2. 栈帧3. 分析4. 总结1. 前言在刚开始学习Java的时候,大家肯定都接触过this关键字,尤其是在构造函数赋值的时候
- java生成json隐藏关键属性今天在工作中遇到一个这样的问题,当后端返回数据时一些关键信息或敏感信息并不想返回到前端,但是又懒得定义专用的
- 一、编码规则Base64编码的思想是是采用64个基本的ASCII码字符对数据进行重新编码。它将需要编码的数据拆分成字节数组。以3个字节为一组
- 1、什么是 ThreadLocal:ThreadLocal,即线程本地变量,如果你创建了一个变量,那么访问这个变量的每个线程都会有这个变量的