Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Unsafe in Java

2025-02-05 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article will explain in detail how to use Unsafe in Java. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.

What is Unsafe?

First of all, we say that the Unsafe class is located in rt.jar under the sun.misc package, Unsafe translation is not secure, this is not to say that this class is not secure, but that developers using Unsafe is not secure, that is, developers are not recommended to use Unsafe directly. And there is no Unsafe source code in the Oracle JDK source package. In fact, most of the classes in the JUC package use Unsafe. It can be said that Unasfe is the cornerstone of java concurrent packages.

How to get the Unsafe object correctly

Let's see how to get the Unsafe object from the source code

Private Unsafe () {}

First of all, the constructor is privatized, which means that we cannot create a Unsafe object through new Unsafe.

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

I also found a static method named getUnsafe with a return value of Unsafe, which seems to get the Unsafe object by calling this method.

To do this, I wrote the following code to test whether this would work.

Public static void main (String [] args) throws Exception {Unsafe unsafe = Unsafe.getUnsafe (); System.out.println (unsafe);}

Who knew the console reported it wrong?

Looking at the error message is an error in terms of permissions, but I think the way for the AtomicBoolean class to get Unsafe is to call the getUnsafe method, which may only allow classes within the JDK to be accessed in this way. Let's not dig deeper here and find another way to get it.

Keep looking at the source code to find a breakthrough.

The first constant in the Unsafe class is private static final Unsafe theUnsafe; decorated with static and final and there is no direct assignment, which means that there must be a static code block that assigns a value to theUnsafe, and then finds it at the bottom of the class.

Static {registerNatives (); Reflection.registerMethodsToFilter (Unsafe.class, new String [] {"getUnsafe"}); theUnsafe = new Unsafe (); ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset (boolean [] .class); ARRAY_BYTE_BASE_OFFSET = theUnsafe.arrayBaseOffset (byte [] .class); ARRAY_SHORT_BASE_OFFSET = theUnsafe.arrayBaseOffset (short [] .class) ARRAY_CHAR_BASE_OFFSET = theUnsafe.arrayBaseOffset (char [] .class); ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset (int [] .class); ARRAY_LONG_BASE_OFFSET = theUnsafe.arrayBaseOffset (long [] .class); ARRAY_FLOAT_BASE_OFFSET = theUnsafe.arrayBaseOffset (float [] .class); ARRAY_DOUBLE_BASE_OFFSET = theUnsafe.arrayBaseOffset (double [] .class) ARRAY_OBJECT_BASE_OFFSET = theUnsafe.arrayBaseOffset (Object [] .class); ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale (boolean [] .class); ARRAY_BYTE_INDEX_SCALE = theUnsafe.arrayIndexScale (byte [] .class); ARRAY_SHORT_INDEX_SCALE = theUnsafe.arrayIndexScale (short [] .class); ARRAY_CHAR_INDEX_SCALE = theUnsafe.arrayIndexScale (char [] .class) ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale (int [] .class); ARRAY_LONG_INDEX_SCALE = theUnsafe.arrayIndexScale (long [] .class); ARRAY_FLOAT_INDEX_SCALE = theUnsafe.arrayIndexScale (float [] .class); ARRAY_DOUBLE_INDEX_SCALE = theUnsafe.arrayIndexScale (double [] .class); ARRAY_OBJECT_INDEX_SCALE = theUnsafe.arrayIndexScale (Object [] .class); ADDRESS_SIZE = theUnsafe.addressSize ();}

The fourth line assigns a value to theUnsafe. That is to say, after the class is loaded, the theUnsafe constant in Unsafe has been assigned to the Unsafe object. If we want to get the Unsafe object, we just need to use reflection to get the theUnsafe property.

/ * get Unsafe * @ throws NoSuchFieldException * @ throws IllegalAccessException * / public static Unsafe getUnsafe () throws NoSuchFieldException, IllegalAccessException {Field field = Unsafe.class.getDeclaredField ("theUnsafe"); / / Private attributes can access field.setAccessible (true); Unsafe unsafe = (Unsafe) field.get (null); System.out.println (unsafe); return unsafe;}

Implementing CAS Lock with Unsafe

CAS is the abbreviation of compare and swap, which is translated into comparison and exchange in Chinese. Classes that begin with Atomic under the juc package are all implemented using CAS locks and do not override the assignment of a variable. We can also use Unsafe to implement CAS locks ourselves.

Interface Counter {void increment (); long getCounter ();} / * use unsafe to implement CAS lock * / static class CasCounter implements Counter {private volatile long counter = 0; private Unsafe unsafe; private long offset; CasCounter () throws NoSuchFieldException, IllegalAccessException {unsafe = getUnsafe () / / get the starting position of the memory offset of this class's counter attribute offset = unsafe.objectFieldOffset (CasCounter.class.getDeclaredField ("counter"));} @ Override public void increment () {long current = counter / / Loop to determine whether the assignment is successful. The first parameter is the object that called this method, the second parameter is the memory offset of the property to be changed, the third parameter is the unmodified value, and the fourth parameter is the value you want to change to that value. While (! unsafe.compareAndSwapLong (this,offset,current,current+1)) {current = counter;} @ Override public long getCounter () {return counter;}}

Here we mainly look at unsafe.objectFieldOffset (CasCounter.class.getDeclaredField ("counter")); this line of code, because changes to property values through CAS are made directly in memory, we need to get the memory offset of the object's counter property.

Let's take a look at the increment method. Here we get the value of counter before changing it. The unsafe.compareAndSwapLong method changes the value according to the memory offset. The first parameter determines which object, the second parameter determines that attribute, the third parameter compares the original value of the attribute to be changed, and the fourth parameter determines the value to be changed. True is returned if the change is successful, and false is returned if the change fails. The logic here is to change all the time when the change fails, and don't jump out of the loop until the change is successful. This will effectively prevent the attribute value from being overwritten. The CasCounter class we wrote implements some of the functions of the AtomicInteger class.

Create objects using Unsafe

We all know that reflection can create objects through the back door, but Unsafe is also possible.

Static class Simple {static {System.out.println (Class initialization);} private long l = 0; public Simple () {this.l = 1; System.out.println (object initialization);} public long get () {return l }} public static void main (String [] args) throws Exception {Unsafe unsafe = getUnsafe (); / / equivalent to opening an address directly in memory without running the constructor Simple simple = (Simple) unsafe.allocateInstance (Simple.class); System.out.println ("creating an object through unsafe does not run the constructor:" + simple.get ()) System.out.println ("but you can get the class object through the object" + simple.getClass (); System.out.println ("you can also get the classloader" + simple.getClass () .getClassLoader ());}

The console output is as follows

Here we find that creating an object using Unsafe does not run the constructor, but just creates the object. Creating an object using reflection will run the constructor in the same way that you create an object using new. It is not recommended to use Unsafe to create objects.

Unsafe load class

Since Unsafe operates directly on memory, it should also be possible to load classes, so let's take a look at how Unsafe loads classes.

Let's write our own class A first.

Public class A {private int i = 0; public A () {this.i = 10;} public int get () {return I;}}

Then run javac A.java to generate A.class. Now the location of A.class is F:\ tmp.

Secondly, we write the code that Unsafe loads class.

/ * * obtain binary * @ return * @ throws IOException * / public static byte [] loadClassContent () throws IOException {File file = new File ("F:\\ tmp\\ a.class"); FileInputStream fis = new FileInputStream (file); byte [] content = new byte [(int) file.length ()]; fis.read (content); fis.close () Return content;} public static void main (String [] args) throws Exception {Unsafe unsafe = getUnsafe (); byte [] bytes = loadClassContent (); Class aClass = unsafe.defineClass (null, bytes, 0, bytes.length,null,null); Method get = aClass.getMethod ("get"); int I = (int) get.invoke (aClass.newInstance (), null); System.out.println (I);}

Here the unsafe.defineClass method is the method that loads the class.

The output result after running is 10.

This makes it possible to load the class through Unsafe.

Unsafe changes private attribute value

We all know that reflection can change the private property value of an object, but Unsafe can also change the private property value directly. The code is as follows

Static class Guard {private int ACCESS_ALLOWED = 1; private boolean allow () {return 42percent access getUnsafe;} public void work () {if (allow ()) {System.out.println ("you operated in the dark");} public static void main (String [] args) throws Exception {Unsafe unsafe = getUnsafe () Guard guard = new Guard (); Field access_allowed = guard.getClass (). GetDeclaredField ("ACCESS_ALLOWED"); unsafe.putInt (guard,unsafe.objectFieldOffset (access_allowed), 42); guard.work ();}

The output is a black box operation for you. The first parameter of the putInt method is which object the property belongs to, the second parameter is the memory offset of the property, and the third parameter is what value to change. It is simply changing the value of the int attribute in the specified memory address directly. So we are done using Unsafe to change the value of the private property of the object.

The fact that the Unsafe class can directly manipulate memory determines that it can go through too many backdoors, and most of the methods are native-decorated, underlying C++ calls. I guess this is also the reason why Unsafe is not safe.

This is the end of the article on "how to use Unsafe in Java". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report