Java中大部分错误都是基于内存管理方面的。如果想破坏,可以使用Unsafe这个类。

实例化Unsafe:

下面两种方式是不行的

private Unsafe() {} //私有构造方法

@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {//如果不是JDK信任的类去实例化,则抛出异常
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}

所以,简单方式就是通过反射去实例化Unsafe

Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

避免初始化

当你想要跳过对象初始化阶段,或绕过构造器的安全检查,或实例化一个没有任何公共构造器的类,allocateInstance方法是非常有用的。

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class App
{
public static void main( String[] args ) throws Exception {
Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
A o1 = new A(); // constructor
o1.a(); // prints 1 A o2 = A.class.newInstance(); // reflection
o2.a(); // prints 1 A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
o3.a(); // prints 0
}
} class A {
private long a; // not initialized value
public A() {
this.a = ; // initialization
}
public void a() {
System.out.println(this.a);
}
}

使用直接获取内存的方式实现浅克隆

如何实现浅克隆?在clone(){...}方法中调用super.clone(),对吗?这里存在的问题是首先你必须继续Cloneable接口,并且在所有你需要做浅克隆的对象中实现clone()方法,对于一个懒懒的程序员来说,这个工作量太大了。

浅克隆

static Object shallowCopy(Object obj) {
long size = sizeOf(obj);
long start = toAddress(obj);
long address = getUnsafe().allocateMemory(size);
getUnsafe().copyMemory(start, address, size);
return fromAddress(address);
}

toAddress和fromAddress将对象转换为其在内存中的地址,反之亦然。

static long toAddress(Object obj) {
Object[] array = new Object[] {obj};
long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
return normalize(getUnsafe().getInt(array, baseOffset));
} static Object fromAddress(long address) {
Object[] array = new Object[] {null};
long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
getUnsafe().putLong(array, baseOffset, address);
return array[0];
}

这个拷贝方法可以用来拷贝任何类型的对象,动态计算它的大小。注意,在拷贝后,你需要将对象转换成特定的类型。

隐藏密码

Unsafe中,一个更有趣的直接内存访问的用法是,从内存中删除不必要的对象。

检索用户密码的大多数API的签名为byte[]char[],为什么是数组呢?

这完全是出于安全的考虑,因为我们可以删除不需要的数组元素。如果将用户密码检索成字符串,这可以像一个对象一样在内存中保存,而删除该对象只需执行解除引用的操作。但是,这个对象仍然在内存中,由GC决定的时间来执行清除。

创建具有相同大小、假的String对象,来取代在内存中原来的String对象的技巧:

String password = new String("l00k@myHor$e");
String fake = new String(password.replaceAll(".", "?"));
System.out.println(password); // l00k@myHor$e
System.out.println(fake); // ???????????? getUnsafe().copyMemory(fake, 0L, null, toAddress(password), sizeOf(password)); System.out.println(password); // ????????????
System.out.println(fake); // ????????????

我们需要通过反射删除后台char数组:

Field stringValue = String.class.getDeclaredField("value");
stringValue.setAccessible(true);
char[] mem = (char[]) stringValue.get(password);
for (int i=0; i < mem.length; i++) {
mem[i] = '?';
}

多继承(Multiple Inheritance)

Java中没有多继承。

这是对的,除非我们可以将任意类型转换成我们想要的其他类型。

long intClassAddress = normalize(getUnsafe().getInt(new Integer(0), 4L));
long strClassAddress = normalize(getUnsafe().getInt("", 4L));
getUnsafe().putAddress(intClassAddress + 36, strClassAddress);

这个代码片段将String类型添加到Integer超类中,因此我们可以强制转换,且没有运行时异常。

(String) (Object) (new Integer(666))

有一个问题,我们必须预先强制转换对象,以欺骗编译器。

动态类(Dynamic classes)

我们可以在运行时创建一个类,比如从已编译的.class文件中。将类内容读取为字节数组,并正确地传递给defineClass方法。

byte[] classContents = getClassContent();
Class c = getUnsafe().defineClass(
null, classContents, 0, classContents.length);
c.getMethod("a").invoke(c.newInstance(), null); //

从定义文件(class文件)中读取(代码)如下:

private static byte[] getClassContent() throws Exception {
File f = new File("/home/mishadoff/tmp/A.class");
FileInputStream input = new FileInputStream(f);
byte[] content = new byte[(int)f.length()];
input.read(content);
input.close();
return content;
}

大数组

正如你所知,Java数组大小的最大值为Integer.MAX_VALUE。使用直接内存分配,我们创建的数组大小受限于堆大小。

SuperArray的实现

class SuperArray {
private final static int BYTE = 1; private long size;
private long address; public SuperArray(long size) {
this.size = size;
address = getUnsafe().allocateMemory(size * BYTE);
} public void set(long i, byte value) {
getUnsafe().putByte(address + i * BYTE, value);
} public int get(long idx) {
return getUnsafe().getByte(address + idx * BYTE);
} public long size() {
return size;
}
}

用法:

long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;
SuperArray array = new SuperArray(SUPER_SIZE);
System.out.println("Array size:" + array.size()); //
for (int i = 0; i < 100; i++) {
array.set((long)Integer.MAX_VALUE + i, (byte)3);
sum += array.get((long)Integer.MAX_VALUE + i);
}
System.out.println("Sum of 100 elements:" + sum); //

实际上,这是堆外内存(off-heap memory)技术,在java.nio包中部分可用。

这种方式的内存分配不在堆上,且不受GC管理,所以必须小心Unsafe.freeMemory()的使用。它也不执行任何边界检查,所以任何非法访问可能会导致JVM崩溃。

这可用于数学计算,代码可操作大数组的数据。此外,这可引起实时程序员的兴趣,可打破GC在大数组上延迟的限制。

结论(Conclusion)

即使Unsafe对应用程序很有用,但(建议)不要使用它。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: Java Magic. Part 4: sun.misc.Unsafe

sun.misc.unsafe的更多相关文章

  1. sun.misc.Unsafe的理解

    以下sun.misc.Unsafe源码和demo基于jdk1.7: 最近在看J.U.C里的源码,很多都用到了sun.misc.Unsafe这个类,一知半解,看起来总感觉有点不尽兴,所以打算对Unsaf ...

  2. Java--如何使用sun.misc.Unsafe完成compareAndSwapObject原子操作

    package com; import sun.misc.Unsafe; import java.lang.reflect.Field; /** * Created by yangyu on 16/1 ...

  3. Java sun.misc.Unsafe类的学习笔记

    Java未开源的Unsafe类 Unsafe类可以为我们提供高效并且线程安全方式操作变量,直接和内存数据打交道. 获取Unsafe实体的方法 private static Unsafe getUnsa ...

  4. java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值

    在上一篇文章中.我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下.如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证 ...

  5. sun.misc.Unsafe 详解

    原文地址 译者:许巧辉 校对:梁海舰 Java是一门安全的编程语言,防止程序员犯很多愚蠢的错误,它们大部分是基于内存管理的.但是,有一种方式可以有意的执行一些不安全.容易犯错的操作,那就是使用Unsa ...

  6. JDK 1.8 sun.misc.Unsafe类CAS底层实现

    在java.util.concurrent包下面的很多类为了追求性能都采用了sun.misc.Unsafe类中的CAS操作,从而避免使用synchronized等加锁方式带来性能上的不足. 在sun. ...

  7. Java中的sun.misc.Unsafe包

    chronicle项目:https://github.com/peter-lawrey/Java-Chronicle 这个项目是利用mmap机制来实现高效的读写数据,号称每秒写入5到20百万条数据. ...

  8. Java sun.misc.unsafe类

    Java是一个安全的开发工具,它阻止开发人员犯很多低级的错误,而大部份的错误都是基于内存管理方面的.如果你想搞破坏,可以使用Unsafe这个类.这个类是属于sun.*API中的类,并且它不是J2SE中 ...

  9. Java Magic. Part 4: sun.misc.Unsafe

    Java Magic. Part 4: sun.misc.Unsafe @(Base)[JDK, Unsafe, magic, 黑魔法] 转载请写明:原文地址 系列文章: -Java Magic. P ...

  10. Understanding sun.misc.Unsafe

    转自: https://dzone.com/articles/understanding-sunmiscunsafe The biggest competitor to the Java virtua ...

随机推荐

  1. dict字典;dict的操作

    一.字典: 1. 字典 dict 用{}来表示 键值对数据 {key:value} 唯一性 键   都必须是可哈希的 不可变的数据类型就可以当做字典中的键 值   没有任何限制 1.1  字典的创建: ...

  2. [ActionScript 3.0] 常用的正则表达式

    as 3.0常用的正则表达式: /* * 去除字符串前面的空格和跳格符 */ var src:String=" Hello! "; trace(src); //原文本 trace( ...

  3. Postman使用手册4——API test

    一.Pre Request Scripts Postman v0.10+ 版本支持pre-request scripts. prerequest script.png pre-request scri ...

  4. 为什么 SQLite 用 C 编写?

    简评:SQLite 官方出品. C是最好的选择 从 2000 年 5 月 29 日开始,SQLite 就选择了 C 语言.直到今天,C 也是实现 SQLite 这样软件库的最佳语言. C语言是实现 S ...

  5. CH2601 电路维修(双端队列bfs)建图恶心

    CH2601 电路维修 双端队列bfs,其实就是因为只有0和1所以可以直接2维护队列单调性(和优先队列一个道理) 建图的过程需要仔细斟酌(想一想id为什么这么写) 还有,空间要开够(很玄学),我一开始 ...

  6. J15W-J45W黄铜截止阀厂家,J15W-J45W黄铜截止阀价格 - 专题栏目 - 无极资讯网

    无极资讯网 首页 最新资讯 最新图集 最新标签   搜索 J15W-J45W黄铜截止阀 无极资讯网精心为您挑选了(J15W-J45W黄铜截止阀)信息,其中包含了(J15W-J45W黄铜截止阀)厂家,( ...

  7. (转)linux shell 数字计算详解

    代码中免不了要进行各种数据计算.抛开科学计算不提,普通的计算占地,百分比,同比,环比等需求就很常见.linux shell中进行数字计算,主要有如下几种方式: 1.bc bc是比较常用的linux计算 ...

  8. Struts2注解详解

    一,引入支持Struts2支持注解开发jar包: struts2-convention-plugin-2.1.8.1.jar(支持Struts2框架注解开发的jar包) 二,Struts2使用注解开发 ...

  9. Android学习系列--App列表之拖拽ListView(上)

    研究了很久的拖拽ListView的实现,受益良多,特此与尔共飨.      鉴于这部分内容网上的资料少而简陋,而具体的实现过程或许对大家才有帮助,为了详尽而不失真,我们一步一步分析,分成两篇文章. 一 ...

  10. c++ 网络编程(八) LINUX-epoll/windows-IOCP下 socket opoll函数用法 优于select方法的epoll 以及windows下IOCP 解决多进程服务端创建进程资源浪费问题

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9622548.html 锲子:关于并发服务器中的I/O复用实现方式,前面在网络编程系列四还是五来 ...