Recently, in order to get a deeper understanding of NIO Implementation principle of , Study NIO Source code when , There's a problem . That is to say WindowsSelectorImpl Medium
pollWrapper attribute , When I click in to see its PollArrayWrapper Type , Find it and AllocatedNativeObject The type is related to , and AllocatedNativeObject Inherited NativeObject class , With the discovery of NativeObject It's based on a Unsafe Class implements the . Unsafe classes ????
Unsafe
Unsafe, seeing the name of a thing one thinks of its function , It's really an insecure class , So why is it unsafe ? This is from Unsafe The function of class .
Did you learn C# You can know ,C# and Java One of the important differences is :C# You can manipulate a memory area directly , If you apply for memory and release , And in the Java It's impossible . and Unsafe Class can let us in Java As in the C# To operate a memory area directly , It is because Unsafe Class can manipulate memory directly , It means it's faster , Under the condition of high concurrency, it can improve the efficiency , therefore java Many concurrent frameworks in , Such as Netty, It's all used Unsafe class .
although ,Unsafe It can improve the running speed , But because Java It doesn't support operating memory directly , That means Unsafe Class does operations that are not subject to jvm Managed , So it won't be GC( Garbage collection ), We need to manually GC, A little carelessness will lead to memory leakage . And Unsafe Many of the methods in must provide the original address ( Memory address ) And the address of the replaced object , The offset should be calculated by yourself , Once there's a problem JVM Crash level exception , Will lead to the whole JVM Instance crash . That's why Unsafe What's called insecurity .Unsafe It's going to get you on the gas pedal , Improve your speed , But it makes it harder to hold your steering wheel , If you're not careful, you may get killed .
Source check
initialization
because Unsafe Is constructed by private Type of , So I can't get through new Method instantiation to get , Only through its getUnsafe() Method to get . Again because Unsafe It operates memory directly , For safety's sake ,Java The developers for Unsafe Set restrictions on the acquisition of , So you can only get it by Java The reflection mechanism to get .
@CallerSensitive public static Unsafe getUnsafe() { // adopt getCallerClass Method to get Unsafe class Class var0 = Reflection.getCallerClass(); // If it should be var0 Class is not the starting class loader (Bootstrap), Throw an exception // Because it's time to judge , therefore Unsafe Only by reflection if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } }
-
Reflection.getCallerClass(): You can return the calling class or Reflection class , Or upload layer by layer
-
VM.isSystemDomainLoader(ClassLoader var0): Determine whether the class loader is a boot class loader (Bootstrap).
-
@CallerSensitive: In order to prevent hackers from enhancing their privileges through double reflection , So all interface methods related to reflection are labeled with CallerSensitive
So you can't get it in the following way Unsafe Class :
// Using this method will throw an exception , Because it is loaded through the system class loader (AppClassLoader) public class Test { public static void main(String[] args) { Unsafe unsafe = Unsafe.getUnsafe(); } }
How to use the startup class to load Unsafe Class and get it ? stay Unsafe At the bottom of the class static There's a piece of code in the code block :
private static final Unsafe theUnsafe; //..... static { registerNatives(); Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"}); theUnsafe = new Unsafe(); //...... }
As we have seen above, we can learn from the reflection mechanism getDeclaredField() Go back to get Un safe Class theUnsafe attribute , And then get... Through this property Unsafe Class , Because in Unsafe Generic theUnsafe Attribute has been new Instantiate .
public class Test { public static void main(String[] args) throws Exception { // adopt getDeclaredField Method to get Unsafe Class named theUnsafe Properties of // Be careful , The attribute is private Type of , So it can't be used getField obtain , Only use getDeclaredField Field field = Unsafe.class.getDeclaredField("theUnsafe"); // Make the property accessible field.setAccessible(true); // Instance the property and change it to Unsafe type // because theUnsafe The attribute is Unsafe Class is loaded by the startup class of the package where the class is located , So you can get Unsafe unsafe = (Unsafe)field.get(null); } }
Get offset method
Offset
In the actual model , Memory is segmented , If you want to get a storage location in memory , You need to know the address of the segment where the storage unit is located ( Segment head ) And offset , Even if you know the actual address of the storage unit . The offset is the actual address and the address of the segment ( Segment head ) Distance of , Offset = Actual address - Section address ( Segment head ).
for instance , Suppose there's a bookshelf , I need to find it from left to right 、 From top to bottom 1024 This book , I can only count in one book , Until you get to number one 1024 Ben , But if I know the number of shelves 4 The first book of the layer is 1000 This book , Then I'll just start from the 1000 The book begins to count , Count to 1024, Just count 1024-1000=24 Ben . ad locum , Bookshelves are memory , The book you're looking for is a storage unit , On the shelf 4 Layers are memory segments , The first 4 The first book on the shelf is the first book on the shelf 1000 This book is a section address ( Segment head ), The first 1024 This book is the physical address , And the offset is the first 1000 This book goes to 1024 The distance of this book 24.
public native long objectFieldOffset(Field var1);
obtain The static Variable var1 The offset .
public native long staticFieldOffset(Field var1);
obtain static state Variable var1 The offset .
public native Object staticFieldBase(Field var1);
obtain static state Variable var1 The actual address of , coordination staticFieldOffset Methods use , You can find the address of the segment where the variable is located
public native int arrayBaseOffset(Class<?> var1);
Get array var1 The offset of the first element in the , The base address of the array .
In memory , The array is stored in a certain offset increment , For example, the actual address of the first element of the array is 24, The offset for the 4, The offset increment of the array is 1, The actual address of the second element of the array is 25, The offset for the 5.
public native int arrayIndexScale(Class<?> var1);
Get array var1 Offset increment of . combination arrayBaseOffset(Class<?> var1) Method can find the address of each element in the array .
Operation property method
public native Object getObject(Object var1, long var2);
obtain var1 The offset in the object is var2 Of Object object , This method can ignore modifier restrictions . In the same way getInt、getLong、getBoolean etc. .
public native void putObject(Object var1, long var2, Object var4);
take var1 The offset in the object is var2 Of Object Set the value of the object var4, This method can ignore modifier restrictions . The same method is putInt、putLong、putBoolean etc. .
public native Object getObjectVolatile(Object var1, long var2);
Function and getObject(Object var1, long var2) equally , But this method can guarantee the reading and writing of Visibility and orderliness , You can ignore modifier restrictions . The same method is getIntVolatile、getLongVolatile、getBooleanVolatile etc. .
public native void putObjectVolatile(Object var1, long var2, Object var4);
Function and putObject(Object var1, long var2, Object var4) equally , But this method can guarantee the reading and writing of Visibility and orderliness , You can ignore modifier restrictions . The same method is putIntVolatile、putLongVolatile、putBooleanVolatile etc. .
public native void putOrderedObject(Object var1, long var2, Object var4);
Function and putObject(Object var1, long var2, Object var4) equally , But this method can guarantee the reading and writing of Orderliness ( Visibility is not guaranteed ), You can ignore modifier restrictions . The same method is putOrderedInt、putOrderedLong etc. .
How to manipulate memory
public native int addressSize();
Get local pointer size , Unit is byte, Usually the value is 4 or 8.
public native int pageSize();
Get the number of pages in local memory , The return value will be 2 Power square .
public native long allocateMemory(long var1);
Open up a new memory block , The size is var1( Unit is byte), Returns the address of the newly opened memory block .
public native long reallocateMemory(long var1, long var3);
The memory address is var3 The size of the memory block is adjusted to var1( Unit is byte), Returns the new memory block address after adjustment .
public native void setMemory(long var2, long var4, byte var6);
From the actual address var2 Start to change the following bytes to var6, Change the size to var4( Usually it is 0).
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
From object var1 The middle offset is var2 Start copying the address of , Copied to the var4 The middle offset is var5 The address of , The size of the copy is var7.
When var1 It's empty time ,var2 It's not the offset, it's the actual address , When var4 It's empty time ,var5 It's not the offset, it's the actual address .
public native void freeMemory(long var1);
The actual address of release is var1 Of memory .
Thread suspension and recovery methods
public native void unpark(Object var1);
Threads that will be suspended var1 recovery , Because of its insecurity , You need to ensure that threads var1 It's alive .
public native void park(boolean var1, long var2);
When var2 be equal to 0 when , The thread will always hang , Know to call unpark Method can restore .
When var2 Greater than 0 when , If var1 by false, At this time var2 For incremental time , The thread is suspended var2 It will recover automatically in seconds , If var1 by true, At this time var2 Is absolute time , After the thread is suspended , Get a specific time var2 And then it's automatically restored .
CAS Method
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
CAS Mechanism related operations , To the object var1 The internal offset is var2 The variable of CAS modify ,var4 For the expected value ,var5 To modify the value , Return the modification result . In the same way compareAndSwapInt、compareAndSwapLong.
Class loading method
public native boolean shouldBeInitialized(Class<?> var1);
Judge var1 Whether the class is initialized .
public native void ensureClassInitialized(Class<?> var1);
Make sure var1 Class has been initialized .
public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);
Define a class , For dynamic creation of classes .var1 For class name ,var2 File data byte array for class ,var3 by var2 Input starting point of ,var4 Is the input length ,var5 To load the loader for this class ,var6 To protect the field . Return the created class .
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);
For dynamic creation of anonymous inner classes .var1 For classes that need to create anonymous inner classes ,var2 File data byte array for anonymous inner class ,var3 For patching objects . Return the created anonymous inner class .
public native Object allocateInstance(Class<?> var1) throws InstantiationException;
establish var1 Class , But will not call var1 Construction method of class , If var1 Class has not yet been initialized , Then initialize . Return to create instance object .
Memory barrier method
public native void loadFence();
All read operations must be in loadFence The method is executed before it is executed .
public native void storeFence();
All write operations must be in storeFence The method is executed before it is executed .
public native void fullFence();
All read and write operations must be in fullFence The method is executed before it is executed .
doubt
There may be a doubt about this , Why do these methods have no specific function implementation code ?
It was said at the beginning of the article that ,Java Direct operation of memory is not supported , How could it be possible to use Java To realize the specific function . You can see that Unsafe Most methods within a class have native Modifier ,native Interface allows you to call local code files ( Including other languages , Such as c Language ), since Java It can't be realized , Let those who can do it , therefore Unsafe The underlying implementation language of is actually C Language , That's why Unsafe There will be offsets and pointers in the class Java There is no concept in .