This is the 19th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

preface

In Java, the most common way to create an instance of an object is to call the corresponding constructor with new. When the constructor is not public, but private, how can we create an instance of an object? Create a class whose constructor is private as follows:

@Setter
@Getter
public class Girlfriend {
    private String name;
    private intThe age;private Girlfriend(String name) {
        this.name = name; }}Copy the code

Let me new one last time……

Reflection create instance

In addition to new, I have reflection. Through reflection, you can get the fields and methods of a class, and you can also get the constructor of a class to create an object. For private modified fields, methods, and class constructors, we must disable security checking to obtain them.

No security inspection is prohibited

Security checking is enabled by default, and methods, classes, and constructors that are private are denied access.Running results:

Prohibition of security inspection

Turn off the security check by setAccessible(true) and access the constructor of the private modifier.

Running results:This is it? This is it? Unsafe, a class that can manipulate memory directly and create objects without a constructor.

sun.mics.Unsafe

With the advent of the JVM, Java no longer has the trouble of C language memory management, but also lost the ability to manipulate memory like Pointers. Unsafe stepped in to fill the gap. But as the name suggests, operating directly on memory is considered unsafe and poses many security issues. Therefore, Unsafe cannot be instantiated by new, and its only constructor is also private. Let’s look at the fields and methods associated with the Unsafe instance in the source code.

The source code is as follows:

public final class Unsafe {
    private static final Unsafe theUnsafe;
    private Unsafe(a) {}@CallerSensitive
    public static Unsafe getUnsafe(a) {
        Class var0 = Reflection.getCallerClass();
        // Check whether the loader calling the class is Bootstrap or null
        if(! VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException("Unsafe");
        } else {
            returntheUnsafe; }}}Copy the code

It’s not hard to see that getUnsafe() is a public method, but it checks to see if the loader that calls getUsafe() is a Bootstrap loader, but the default loader that defines the class is AppClassLoader, so it throws an exception directly.

Check the class loader code as follows:

	// The bootstrap loader is responsible for loading rt.jar, not written in Java, so null
	public static boolean isSystemDomainLoader(ClassLoader var0) {
        return var0 == null;
    }
Copy the code

Therefore, we can only create instances from the theUnsafe field and constructor by reflection.

The code is as follows:

public class Boy {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class<Unsafe> unsafeClass = Unsafe.class;
        // The first way: get the Unsafe instance through the constructor
        Constructor<Unsafe> declaredConstructor = unsafeClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Unsafe unsafe1 = declaredConstructor.newInstance();

        // The second method: get the Unsafe instance from the field
        Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe2 = (Unsafe)theUnsafe.get(null); }}Copy the code

There are two ways to get to the Unsafe instance object, and now let’s look at how to build the instance.

AllocateInstance () : Build the instance

With unsafe instances, you can use this method to create instance objects directly without using a constructor.

  // Parsing parsing parsing parsing; // parsing parsing parsing parsing parsing parsing
  Girlfriend girlfriend1 = (Girlfriend) unsafe1.allocateInstance(Girlfriend.class);
  Girlfriend girlfriend2 = (Girlfriend) unsafe2.allocateInstance(Girlfriend.class);
  girlfriend1.setName("Small fang 1");
  girlfriend2.setName("Small fang 2");
  System.out.println(girlfriend1.getName());
  System.out.println(girlfriend2.getName());
Copy the code

Running results:As you can see from the results, the unsafe object obtained in both ways can be used to create instance objects. So what else does Unsafe do? Unsafe now offers more than 100 methods, so here are a few to put to the test.

PutObject () : Modifies object member variables

 Field nameField = Girlfriend.class.getDeclaredField("name");
 System.out.println(girlfriend1.getName());
 // Get the offset of the name member variable's memory address relative to the object's memory address
 long l = unsafe1.objectFieldOffset(nameField);
 // Modify the value of the member variable name
 unsafe1.putObject(girlfriend1, l, "Little red");
 System.out.println(girlfriend1.getName());
Copy the code

Running results:

CompareAndSwapInt () : Atom modifies the int property value

Those interested can learn about the CAS principle inside the lock

Field ageField = Girlfriend.class.getDeclaredField("age");
long l1 = unsafe1.objectFieldOffset(ageField);
girlfriend1.setAge(20);
System.out.println("Initial age:" + girlfriend1.getAge());
// cas operation, the third parameter must be the old value, that is, 20, otherwise the modification fails.
unsafe1.compareAndSwapInt(girlfriend1, l1, 20.18);
System.out.println("Modified age:" +girlfriend1.getAge());
// Get the int value from the offset
int i = unsafe1.getInt(girlfriend1, l1);
System.out.println("Offset gets int value:" + i);

Copy the code

Running results:

AllocateMemory () : Allocates memory

// Allocate an 8byte memory and return the entry address
long address = unsafe1.allocateMemory(8L);
// initialize 8 bytes from the entry address with 0000 0000 representing a byte
unsafe1.setMemory(address, 8L, (byte) 0);
/ / aLong to 0
long aLong1 = unsafe1.getLong(address);
// Change address to long (8byte)
unsafe1.putLong(address,100L);
/ / aLong2 for 100
long aLong2 = unsafe1.getLong(address);
Copy the code

Netty application scenarios

PlatformDependent0 allocates memory and retrievals variables over Unsafe, since the Netty framework defines its own memory allocation management system.

conclusion

Unsafe also wants to explore other approaches, too. Its core method is mainly around the memory address entry, address offset to launch, please use carefully in the development.