View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import io.netty.util.internal.logging.InternalLogger;
19  import io.netty.util.internal.logging.InternalLoggerFactory;
20  import sun.misc.Unsafe;
21  
22  import java.lang.invoke.MethodHandle;
23  import java.lang.invoke.MethodHandles;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.Field;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.nio.Buffer;
29  import java.nio.ByteBuffer;
30  import java.security.AccessController;
31  import java.security.PrivilegedAction;
32  import java.util.concurrent.atomic.AtomicLong;
33  
34  import static java.lang.invoke.MethodType.methodType;
35  
36  /**
37   * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
38   */
39  final class PlatformDependent0 {
40  
41      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
42      private static final long ADDRESS_FIELD_OFFSET;
43      private static final long BYTE_ARRAY_BASE_OFFSET;
44      private static final long INT_ARRAY_BASE_OFFSET;
45      private static final long INT_ARRAY_INDEX_SCALE;
46      private static final long LONG_ARRAY_BASE_OFFSET;
47      private static final long LONG_ARRAY_INDEX_SCALE;
48      private static final MethodHandle DIRECT_BUFFER_CONSTRUCTOR;
49      private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
50      private static final MethodHandle ALLOCATE_ARRAY_METHOD;
51      private static final MethodHandle ALIGN_SLICE;
52      private static final int JAVA_VERSION = javaVersion0();
53      private static final boolean IS_ANDROID = isAndroid0();
54  
55      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
56  
57      // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/
58      // ImageInfo.java
59      private static final boolean RUNNING_IN_NATIVE_IMAGE = SystemPropertyUtil.contains(
60              "org.graalvm.nativeimage.imagecode");
61  
62      private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
63  
64      static final Unsafe UNSAFE;
65  
66      // constants borrowed from murmur3
67      static final int HASH_CODE_ASCII_SEED = 0xc2b2ae35;
68      static final int HASH_CODE_C1 = 0xcc9e2d51;
69      static final int HASH_CODE_C2 = 0x1b873593;
70  
71      /**
72       * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
73       * during a large copy.
74       */
75      private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
76  
77      private static final boolean UNALIGNED;
78  
79      private static final long BITS_MAX_DIRECT_MEMORY;
80  
81      static {
82          MethodHandles.Lookup lookup = MethodHandles.lookup();
83          final ByteBuffer direct;
84          Field addressField = null;
85          MethodHandle allocateArrayMethod = null;
86          Throwable unsafeUnavailabilityCause;
87          Unsafe unsafe;
88          if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
89              direct = null;
90              addressField = null;
91              unsafe = null;
92          } else {
93              direct = ByteBuffer.allocateDirect(1);
94  
95              // attempt to access field Unsafe#theUnsafe
96              final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
97                  @Override
98                  public Object run() {
99                      try {
100                         final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
101                         // We always want to try using Unsafe as the access still works on java9 as well and
102                         // we need it for out native-transports and many optimizations.
103                         Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
104                         if (cause != null) {
105                             return cause;
106                         }
107                         // the unsafe instance
108                         return unsafeField.get(null);
109                     } catch (NoSuchFieldException e) {
110                         return e;
111                     } catch (SecurityException e) {
112                         return e;
113                     } catch (IllegalAccessException e) {
114                         return e;
115                     } catch (NoClassDefFoundError e) {
116                         // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
117                         // Unsafe unloadable.
118                         return e;
119                     }
120                 }
121             });
122 
123             // the conditional check here can not be replaced with checking that maybeUnsafe
124             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
125             // instanceof check against Unsafe will trigger a class load and we might not have
126             // the runtime permission accessClassInPackage.sun.misc
127             if (maybeUnsafe instanceof Throwable) {
128                 unsafe = null;
129                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
130                 if (logger.isTraceEnabled()) {
131                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Throwable) maybeUnsafe);
132                 } else {
133                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable: {}", unsafeUnavailabilityCause.getMessage());
134                 }
135             } else {
136                 unsafe = (Unsafe) maybeUnsafe;
137                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
138             }
139 
140             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK,
141             // or that they haven't been removed by JEP 471.
142             // https://github.com/netty/netty/issues/1061
143             // https://www.mail-archive.com/[email protected]/msg00698.html
144             // https://openjdk.org/jeps/471
145             if (unsafe != null) {
146                 final Unsafe finalUnsafe = unsafe;
147                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
148                     @Override
149                     public Object run() {
150                         try {
151                             // Other methods like storeFence() and invokeCleaner() are tested for elsewhere.
152                             Class<? extends Unsafe> cls = finalUnsafe.getClass();
153                             cls.getDeclaredMethod(
154                                     "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
155                             if (javaVersion() > 23) {
156                                 cls.getDeclaredMethod("objectFieldOffset", Field.class);
157                                 cls.getDeclaredMethod("staticFieldOffset", Field.class);
158                                 cls.getDeclaredMethod("staticFieldBase", Field.class);
159                                 cls.getDeclaredMethod("arrayBaseOffset", Class.class);
160                                 cls.getDeclaredMethod("arrayIndexScale", Class.class);
161                                 cls.getDeclaredMethod("allocateMemory", long.class);
162                                 cls.getDeclaredMethod("reallocateMemory", long.class, long.class);
163                                 cls.getDeclaredMethod("freeMemory", long.class);
164                                 cls.getDeclaredMethod("setMemory", long.class, long.class, byte.class);
165                                 cls.getDeclaredMethod("setMemory", Object.class, long.class, long.class, byte.class);
166                                 cls.getDeclaredMethod("getBoolean", Object.class, long.class);
167                                 cls.getDeclaredMethod("getByte", long.class);
168                                 cls.getDeclaredMethod("getByte", Object.class, long.class);
169                                 cls.getDeclaredMethod("getInt", long.class);
170                                 cls.getDeclaredMethod("getInt", Object.class, long.class);
171                                 cls.getDeclaredMethod("getLong", long.class);
172                                 cls.getDeclaredMethod("getLong", Object.class, long.class);
173                                 cls.getDeclaredMethod("putByte", long.class, byte.class);
174                                 cls.getDeclaredMethod("putByte", Object.class, long.class, byte.class);
175                                 cls.getDeclaredMethod("putInt", long.class, int.class);
176                                 cls.getDeclaredMethod("putInt", Object.class, long.class, int.class);
177                                 cls.getDeclaredMethod("putLong", long.class, long.class);
178                                 cls.getDeclaredMethod("putLong", Object.class, long.class, long.class);
179                                 cls.getDeclaredMethod("addressSize");
180                             }
181                             if (javaVersion() >= 23) {
182                                 // The following tests the methods are usable.
183                                 // Will throw UnsupportedOperationException if unsafe memory access is denied:
184                                 long address = finalUnsafe.allocateMemory(8);
185                                 finalUnsafe.putLong(address, 42);
186                                 finalUnsafe.freeMemory(address);
187                             }
188                             return null;
189                         } catch (UnsupportedOperationException e) {
190                             return e;
191                         } catch (NoSuchMethodException e) {
192                             return e;
193                         } catch (SecurityException e) {
194                             return e;
195                         }
196                     }
197                 });
198 
199                 if (maybeException == null) {
200                     logger.debug("sun.misc.Unsafe base methods: all available");
201                 } else {
202                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
203                     unsafe = null;
204                     unsafeUnavailabilityCause = (Throwable) maybeException;
205                     if (logger.isTraceEnabled()) {
206                         logger.debug("sun.misc.Unsafe method unavailable:", unsafeUnavailabilityCause);
207                     } else {
208                         logger.debug("sun.misc.Unsafe method unavailable: {}",
209                                 ((Throwable) maybeException).getMessage());
210                     }
211                 }
212             }
213 
214             if (unsafe != null) {
215                 final Unsafe finalUnsafe = unsafe;
216 
217                 // attempt to access field Buffer#address
218                 final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
219                     @Override
220                     public Object run() {
221                         try {
222                             final Field field = Buffer.class.getDeclaredField("address");
223                             // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
224                             // will forbid changing the access level via reflection.
225                             final long offset = finalUnsafe.objectFieldOffset(field);
226                             final long address = finalUnsafe.getLong(direct, offset);
227 
228                             // if direct really is a direct buffer, address will be non-zero
229                             if (address == 0) {
230                                 return null;
231                             }
232                             return field;
233                         } catch (NoSuchFieldException e) {
234                             return e;
235                         } catch (SecurityException e) {
236                             return e;
237                         }
238                     }
239                 });
240 
241                 if (maybeAddressField instanceof Field) {
242                     addressField = (Field) maybeAddressField;
243                     logger.debug("java.nio.Buffer.address: available");
244                 } else {
245                     unsafeUnavailabilityCause = (Throwable) maybeAddressField;
246                     if (logger.isTraceEnabled()) {
247                         logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
248                     } else {
249                         logger.debug("java.nio.Buffer.address: unavailable: {}",
250                                 ((Throwable) maybeAddressField).getMessage());
251                     }
252 
253                     // If we cannot access the address of a direct buffer, there's no point of using unsafe.
254                     // Let's just pretend unsafe is unavailable for overall simplicity.
255                     unsafe = null;
256                 }
257             }
258 
259             if (unsafe != null) {
260                 // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and
261                 // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case.
262                 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
263                 if (byteArrayIndexScale != 1) {
264                     logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
265                     unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
266                     unsafe = null;
267                 }
268             }
269         }
270         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
271         UNSAFE = unsafe;
272 
273         if (unsafe == null) {
274             ADDRESS_FIELD_OFFSET = -1;
275             BYTE_ARRAY_BASE_OFFSET = -1;
276             LONG_ARRAY_BASE_OFFSET = -1;
277             LONG_ARRAY_INDEX_SCALE = -1;
278             INT_ARRAY_BASE_OFFSET = -1;
279             INT_ARRAY_INDEX_SCALE = -1;
280             UNALIGNED = false;
281             BITS_MAX_DIRECT_MEMORY = -1;
282             DIRECT_BUFFER_CONSTRUCTOR = null;
283             ALLOCATE_ARRAY_METHOD = null;
284         } else {
285             MethodHandle directBufferConstructor;
286             long address = -1;
287             try {
288                 final Object maybeDirectBufferConstructor =
289                         AccessController.doPrivileged(new PrivilegedAction<Object>() {
290                             @Override
291                             public Object run() {
292                                 try {
293                                     Class<? extends ByteBuffer> directClass = direct.getClass();
294                                     final Constructor<?> constructor = javaVersion() >= 21 ?
295                                             directClass.getDeclaredConstructor(long.class, long.class) :
296                                             directClass.getDeclaredConstructor(long.class, int.class);
297                                     Throwable cause = ReflectionUtil.trySetAccessible(constructor, true);
298                                     if (cause != null) {
299                                         return cause;
300                                     }
301                                     return lookup.unreflectConstructor(constructor)
302                                             .asType(methodType(ByteBuffer.class, long.class, int.class));
303                                 } catch (Throwable e) {
304                                     return e;
305                                 }
306                             }
307                         });
308 
309                 if (maybeDirectBufferConstructor instanceof MethodHandle) {
310                     address = UNSAFE.allocateMemory(1);
311                     // try to use the constructor now
312                     try {
313                         MethodHandle constructor = (MethodHandle) maybeDirectBufferConstructor;
314                         ByteBuffer ignore = (ByteBuffer) constructor.invokeExact(address, 1);
315                         directBufferConstructor = constructor;
316                         logger.debug("direct buffer constructor: available");
317                     } catch (Throwable e) {
318                         directBufferConstructor = null;
319                     }
320                 } else {
321                     if (logger.isTraceEnabled()) {
322                         logger.debug("direct buffer constructor: unavailable",
323                                 (Throwable) maybeDirectBufferConstructor);
324                     } else {
325                         logger.debug("direct buffer constructor: unavailable: {}",
326                                 ((Throwable) maybeDirectBufferConstructor).getMessage());
327                     }
328                     directBufferConstructor = null;
329                 }
330             } finally {
331                 if (address != -1) {
332                     UNSAFE.freeMemory(address);
333                 }
334             }
335             DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
336             ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
337             BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
338             INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
339             INT_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(int[].class);
340             LONG_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
341             LONG_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(long[].class);
342             final boolean unaligned;
343             // using a known type to avoid loading new classes
344             final AtomicLong maybeMaxMemory = new AtomicLong(-1);
345             Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() {
346                 @Override
347                 public Object run() {
348                     try {
349                         Class<?> bitsClass =
350                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
351                         int version = javaVersion();
352                         if (unsafeStaticFieldOffsetSupported() && version >= 9) {
353                             // Java9/10 use all lowercase and later versions all uppercase.
354                             String fieldName = version >= 11? "MAX_MEMORY" : "maxMemory";
355                             // On Java9 and later we try to directly access the field as we can do this without
356                             // adjust the accessible levels.
357                             try {
358                                 Field maxMemoryField = bitsClass.getDeclaredField(fieldName);
359                                 if (maxMemoryField.getType() == long.class) {
360                                     long offset = UNSAFE.staticFieldOffset(maxMemoryField);
361                                     Object object = UNSAFE.staticFieldBase(maxMemoryField);
362                                     maybeMaxMemory.lazySet(UNSAFE.getLong(object, offset));
363                                 }
364                             } catch (Throwable ignore) {
365                                 // ignore if can't access
366                             }
367                             fieldName = version >= 11? "UNALIGNED" : "unaligned";
368                             try {
369                                 Field unalignedField = bitsClass.getDeclaredField(fieldName);
370                                 if (unalignedField.getType() == boolean.class) {
371                                     long offset = UNSAFE.staticFieldOffset(unalignedField);
372                                     Object object = UNSAFE.staticFieldBase(unalignedField);
373                                     return UNSAFE.getBoolean(object, offset);
374                                 }
375                                 // There is something unexpected stored in the field,
376                                 // let us fall-back and try to use a reflective method call as last resort.
377                             } catch (NoSuchFieldException ignore) {
378                                 // We did not find the field we expected, move on.
379                             }
380                         }
381                         Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
382                         Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
383                         if (cause != null) {
384                             return cause;
385                         }
386                         return unalignedMethod.invoke(null);
387                     } catch (NoSuchMethodException e) {
388                         return e;
389                     } catch (SecurityException e) {
390                         return e;
391                     } catch (IllegalAccessException e) {
392                         return e;
393                     } catch (ClassNotFoundException e) {
394                         return e;
395                     } catch (InvocationTargetException e) {
396                         return e;
397                     }
398                 }
399             });
400 
401             if (maybeUnaligned instanceof Boolean) {
402                 unaligned = (Boolean) maybeUnaligned;
403                 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
404             } else {
405                 String arch = SystemPropertyUtil.get("os.arch", "");
406                 //noinspection DynamicRegexReplaceableByCompiledPattern
407                 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
408                 Throwable t = (Throwable) maybeUnaligned;
409                 if (logger.isTraceEnabled()) {
410                     logger.debug("java.nio.Bits.unaligned: unavailable, {}", unaligned, t);
411                 } else {
412                     logger.debug("java.nio.Bits.unaligned: unavailable, {}, {}", unaligned, t.getMessage());
413                 }
414             }
415 
416             UNALIGNED = unaligned;
417             BITS_MAX_DIRECT_MEMORY = maybeMaxMemory.get() >= 0? maybeMaxMemory.get() : -1;
418 
419             if (javaVersion() >= 9) {
420                 Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
421                     @Override
422                     public Object run() {
423                         try {
424                             // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to
425                             // sun.misc.Unsafe
426                             Class<?> cls = getClassLoader(PlatformDependent0.class)
427                                     .loadClass("jdk.internal.misc.Unsafe");
428                             return lookup.findStatic(cls, "getUnsafe", methodType(cls)).invoke();
429                         } catch (Throwable e) {
430                             return e;
431                         }
432                     }
433                 });
434                 if (!(maybeException instanceof Throwable)) {
435                     final Object finalInternalUnsafe = maybeException;
436                     maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
437                         @Override
438                         public Object run() {
439                             try {
440                                 Class<?> finalInternalUnsafeClass = finalInternalUnsafe.getClass();
441                                 return lookup.findVirtual(
442                                         finalInternalUnsafeClass,
443                                         "allocateUninitializedArray",
444                                         methodType(Object.class, Class.class, int.class));
445                             } catch (Throwable e) {
446                                 return e;
447                             }
448                         }
449                     });
450 
451                     if (maybeException instanceof MethodHandle) {
452                         try {
453                             MethodHandle m = (MethodHandle) maybeException;
454                             m = m.bindTo(finalInternalUnsafe);
455                             byte[] bytes = (byte[]) m.invokeExact(byte.class, 8);
456                             assert bytes.length == 8;
457                             allocateArrayMethod = m;
458                         } catch (Throwable e) {
459                             maybeException = e;
460                         }
461                     }
462                 }
463 
464                 if (maybeException instanceof Throwable) {
465                     if (logger.isTraceEnabled()) {
466                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
467                                 (Throwable) maybeException);
468                     } else {
469                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable: {}",
470                                 ((Throwable) maybeException).getMessage());
471                     }
472                 } else {
473                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
474                 }
475             } else {
476                 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
477             }
478             ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
479         }
480 
481         if (javaVersion() > 9) {
482             ALIGN_SLICE = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
483                 @Override
484                 public Object run() {
485                     try {
486                         return MethodHandles.publicLookup().findVirtual(
487                                 ByteBuffer.class, "alignedSlice", methodType(ByteBuffer.class, int.class));
488                     } catch (Throwable e) {
489                         return null;
490                     }
491                 }
492             });
493         } else {
494             ALIGN_SLICE = null;
495         }
496 
497         logger.debug("java.nio.DirectByteBuffer.<init>(long, {int,long}): {}",
498                 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
499     }
500 
501     private static boolean unsafeStaticFieldOffsetSupported() {
502         return !RUNNING_IN_NATIVE_IMAGE;
503     }
504 
505     static boolean isExplicitNoUnsafe() {
506         return EXPLICIT_NO_UNSAFE_CAUSE != null;
507     }
508 
509     private static Throwable explicitNoUnsafeCause0() {
510         boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
511         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
512 
513         // See JDK 23 JEP 471 https://openjdk.org/jeps/471 and sun.misc.Unsafe.beforeMemoryAccess() on JDK 23+.
514         String unsafeMemoryAccess = SystemPropertyUtil.get("sun.misc.unsafe.memory.access", "<unspecified>");
515         if (!("allow".equals(unsafeMemoryAccess) || "<unspecified>".equals(unsafeMemoryAccess))) {
516             logger.debug("--sun-misc-unsafe-memory-access={}", unsafeMemoryAccess);
517             noUnsafe = true;
518         }
519 
520         if (noUnsafe) {
521             logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
522             return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
523         }
524 
525         // Legacy properties
526         String unsafePropName;
527         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
528             unsafePropName = "io.netty.tryUnsafe";
529         } else {
530             unsafePropName = "org.jboss.netty.tryUnsafe";
531         }
532 
533         if (!SystemPropertyUtil.getBoolean(unsafePropName, true)) {
534             String msg = "sun.misc.Unsafe: unavailable (" + unsafePropName + ')';
535             logger.debug(msg);
536             return new UnsupportedOperationException(msg);
537         }
538 
539         return null;
540     }
541 
542     static boolean isUnaligned() {
543         return UNALIGNED;
544     }
545 
546     /**
547      * Any value >= 0 should be considered as a valid max direct memory value.
548      */
549     static long bitsMaxDirectMemory() {
550         return BITS_MAX_DIRECT_MEMORY;
551     }
552 
553     static boolean hasUnsafe() {
554         return UNSAFE != null;
555     }
556 
557     static Throwable getUnsafeUnavailabilityCause() {
558         return UNSAFE_UNAVAILABILITY_CAUSE;
559     }
560 
561     static boolean unalignedAccess() {
562         return UNALIGNED;
563     }
564 
565     static void throwException(Throwable cause) {
566         throwException0(cause);
567     }
568 
569     @SuppressWarnings("unchecked")
570     private static <E extends Throwable> void throwException0(Throwable t) throws E {
571         throw (E) t;
572     }
573 
574     static boolean hasDirectBufferNoCleanerConstructor() {
575         return DIRECT_BUFFER_CONSTRUCTOR != null;
576     }
577 
578     static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
579         return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity);
580     }
581 
582     static ByteBuffer allocateDirectNoCleaner(int capacity) {
583         // Calling malloc with capacity of 0 may return a null ptr or a memory address that can be used.
584         // Just use 1 to make it safe to use in all cases:
585         // See: https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html
586         return newDirectBuffer(UNSAFE.allocateMemory(Math.max(1, capacity)), capacity);
587     }
588 
589     static boolean hasAlignSliceMethod() {
590         return ALIGN_SLICE != null;
591     }
592 
593     static ByteBuffer alignSlice(ByteBuffer buffer, int alignment) {
594         try {
595             return (ByteBuffer) ALIGN_SLICE.invokeExact(buffer, alignment);
596         } catch (Throwable e) {
597             rethrowIfPossible(e);
598             throw new LinkageError("ByteBuffer.alignedSlice not available", e);
599         }
600     }
601 
602     static boolean hasAllocateArrayMethod() {
603         return ALLOCATE_ARRAY_METHOD != null;
604     }
605 
606     static byte[] allocateUninitializedArray(int size) {
607         try {
608             return (byte[]) ALLOCATE_ARRAY_METHOD.invokeExact(byte.class, size);
609         } catch (Throwable e) {
610             rethrowIfPossible(e);
611             throw new LinkageError("Unsafe.allocateUninitializedArray not available", e);
612         }
613     }
614 
615     static ByteBuffer newDirectBuffer(long address, int capacity) {
616         ObjectUtil.checkPositiveOrZero(capacity, "capacity");
617 
618         try {
619             return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.invokeExact(address, capacity);
620         } catch (Throwable cause) {
621             rethrowIfPossible(cause);
622             throw new LinkageError("DirectByteBuffer constructor not available", cause);
623         }
624     }
625 
626     private static void rethrowIfPossible(Throwable cause) {
627         if (cause instanceof Error) {
628             throw (Error) cause;
629         }
630         if (cause instanceof RuntimeException) {
631             throw (RuntimeException) cause;
632         }
633     }
634 
635     static long directBufferAddress(ByteBuffer buffer) {
636         return getLong(buffer, ADDRESS_FIELD_OFFSET);
637     }
638 
639     static long byteArrayBaseOffset() {
640         return BYTE_ARRAY_BASE_OFFSET;
641     }
642 
643     static Object getObject(Object object, long fieldOffset) {
644         return UNSAFE.getObject(object, fieldOffset);
645     }
646 
647     static int getInt(Object object, long fieldOffset) {
648         return UNSAFE.getInt(object, fieldOffset);
649     }
650 
651     static void safeConstructPutInt(Object object, long fieldOffset, int value) {
652         UNSAFE.putInt(object, fieldOffset, value);
653         UNSAFE.storeFence();
654     }
655 
656     private static long getLong(Object object, long fieldOffset) {
657         return UNSAFE.getLong(object, fieldOffset);
658     }
659 
660     static long objectFieldOffset(Field field) {
661         return UNSAFE.objectFieldOffset(field);
662     }
663 
664     static byte getByte(long address) {
665         return UNSAFE.getByte(address);
666     }
667 
668     static short getShort(long address) {
669         return UNSAFE.getShort(address);
670     }
671 
672     static int getInt(long address) {
673         return UNSAFE.getInt(address);
674     }
675 
676     static long getLong(long address) {
677         return UNSAFE.getLong(address);
678     }
679 
680     static byte getByte(byte[] data, int index) {
681         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
682     }
683 
684     static byte getByte(byte[] data, long index) {
685         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
686     }
687 
688     static short getShort(byte[] data, int index) {
689         return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
690     }
691 
692     static int getInt(byte[] data, int index) {
693         return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
694     }
695 
696     static int getInt(int[] data, long index) {
697         return UNSAFE.getInt(data, INT_ARRAY_BASE_OFFSET + INT_ARRAY_INDEX_SCALE * index);
698     }
699 
700     static int getIntVolatile(long address) {
701         return UNSAFE.getIntVolatile(null, address);
702     }
703 
704     static void putIntOrdered(long adddress, int newValue) {
705         UNSAFE.putOrderedInt(null, adddress, newValue);
706     }
707 
708     static long getLong(byte[] data, int index) {
709         return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
710     }
711 
712     static long getLong(long[] data, long index) {
713         return UNSAFE.getLong(data, LONG_ARRAY_BASE_OFFSET + LONG_ARRAY_INDEX_SCALE * index);
714     }
715 
716     static void putByte(long address, byte value) {
717         UNSAFE.putByte(address, value);
718     }
719 
720     static void putShort(long address, short value) {
721         UNSAFE.putShort(address, value);
722     }
723 
724     static void putShortOrdered(long adddress, short newValue) {
725         UNSAFE.storeFence();
726         UNSAFE.putShort(null, adddress, newValue);
727     }
728 
729     static void putInt(long address, int value) {
730         UNSAFE.putInt(address, value);
731     }
732 
733     static void putLong(long address, long value) {
734         UNSAFE.putLong(address, value);
735     }
736 
737     static void putByte(byte[] data, int index, byte value) {
738         UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
739     }
740 
741     static void putByte(Object data, long offset, byte value) {
742         UNSAFE.putByte(data, offset, value);
743     }
744 
745     static void putShort(byte[] data, int index, short value) {
746         UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
747     }
748 
749     static void putInt(byte[] data, int index, int value) {
750         UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
751     }
752 
753     static void putLong(byte[] data, int index, long value) {
754         UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
755     }
756 
757     static void putObject(Object o, long offset, Object x) {
758         UNSAFE.putObject(o, offset, x);
759     }
760 
761     static void copyMemory(long srcAddr, long dstAddr, long length) {
762         // Manual safe-point polling is only needed prior Java9:
763         // See https://bugs.openjdk.java.net/browse/JDK-8149596
764         if (javaVersion() <= 8) {
765             copyMemoryWithSafePointPolling(srcAddr, dstAddr, length);
766         } else {
767             UNSAFE.copyMemory(srcAddr, dstAddr, length);
768         }
769     }
770 
771     private static void copyMemoryWithSafePointPolling(long srcAddr, long dstAddr, long length) {
772         while (length > 0) {
773             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
774             UNSAFE.copyMemory(srcAddr, dstAddr, size);
775             length -= size;
776             srcAddr += size;
777             dstAddr += size;
778         }
779     }
780 
781     static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
782         // Manual safe-point polling is only needed prior Java9:
783         // See https://bugs.openjdk.java.net/browse/JDK-8149596
784         if (javaVersion() <= 8) {
785             copyMemoryWithSafePointPolling(src, srcOffset, dst, dstOffset, length);
786         } else {
787             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
788         }
789     }
790 
791     private static void copyMemoryWithSafePointPolling(
792             Object src, long srcOffset, Object dst, long dstOffset, long length) {
793         while (length > 0) {
794             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
795             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
796             length -= size;
797             srcOffset += size;
798             dstOffset += size;
799         }
800     }
801 
802     static void setMemory(long address, long bytes, byte value) {
803         UNSAFE.setMemory(address, bytes, value);
804     }
805 
806     static void setMemory(Object o, long offset, long bytes, byte value) {
807         UNSAFE.setMemory(o, offset, bytes, value);
808     }
809 
810     static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
811         int remainingBytes = length & 7;
812         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
813         final long diff = startPos2 - startPos1;
814         if (length >= 8) {
815             final long end = baseOffset1 + remainingBytes;
816             for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
817                 if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, i + diff)) {
818                     return false;
819                 }
820             }
821         }
822         if (remainingBytes >= 4) {
823             remainingBytes -= 4;
824             long pos = baseOffset1 + remainingBytes;
825             if (UNSAFE.getInt(bytes1, pos) != UNSAFE.getInt(bytes2, pos + diff)) {
826                 return false;
827             }
828         }
829         final long baseOffset2 = baseOffset1 + diff;
830         if (remainingBytes >= 2) {
831             return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) &&
832                     (remainingBytes == 2 ||
833                     UNSAFE.getByte(bytes1, baseOffset1 + 2) == UNSAFE.getByte(bytes2, baseOffset2 + 2));
834         }
835         return remainingBytes == 0 ||
836                 UNSAFE.getByte(bytes1, baseOffset1) == UNSAFE.getByte(bytes2, baseOffset2);
837     }
838 
839     static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
840         long result = 0;
841         long remainingBytes = length & 7;
842         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
843         final long end = baseOffset1 + remainingBytes;
844         final long diff = startPos2 - startPos1;
845         for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
846             result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, i + diff);
847         }
848         if (remainingBytes >= 4) {
849             result |= UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset1 + diff);
850             remainingBytes -= 4;
851         }
852         if (remainingBytes >= 2) {
853             long pos = end - remainingBytes;
854             result |= UNSAFE.getChar(bytes1, pos) ^ UNSAFE.getChar(bytes2, pos + diff);
855             remainingBytes -= 2;
856         }
857         if (remainingBytes == 1) {
858             long pos = end - 1;
859             result |= UNSAFE.getByte(bytes1, pos) ^ UNSAFE.getByte(bytes2, pos + diff);
860         }
861         return ConstantTimeUtils.equalsConstantTime(result, 0);
862     }
863 
864     static boolean isZero(byte[] bytes, int startPos, int length) {
865         if (length <= 0) {
866             return true;
867         }
868         final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
869         int remainingBytes = length & 7;
870         final long end = baseOffset + remainingBytes;
871         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
872             if (UNSAFE.getLong(bytes, i) != 0) {
873                 return false;
874             }
875         }
876 
877         if (remainingBytes >= 4) {
878             remainingBytes -= 4;
879             if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
880                 return false;
881             }
882         }
883         if (remainingBytes >= 2) {
884             return UNSAFE.getChar(bytes, baseOffset) == 0 &&
885                     (remainingBytes == 2 || bytes[startPos + 2] == 0);
886         }
887         return bytes[startPos] == 0;
888     }
889 
890     static int hashCodeAscii(byte[] bytes, int startPos, int length) {
891         int hash = HASH_CODE_ASCII_SEED;
892         long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
893         final int remainingBytes = length & 7;
894         final long end = baseOffset + remainingBytes;
895         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
896             hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash);
897         }
898         if (remainingBytes == 0) {
899             return hash;
900         }
901         int hcConst = HASH_CODE_C1;
902         if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7
903             hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset));
904             hcConst = HASH_CODE_C2;
905             baseOffset++;
906         }
907         if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7
908             hash = hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset));
909             hcConst = hcConst == HASH_CODE_C1 ? HASH_CODE_C2 : HASH_CODE_C1;
910             baseOffset += 2;
911         }
912         if (remainingBytes >= 4) { // 4, 5, 6, 7
913             return hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset));
914         }
915         return hash;
916     }
917 
918     static int hashCodeAsciiCompute(long value, int hash) {
919         // masking with 0x1f reduces the number of overall bits that impact the hash code but makes the hash
920         // code the same regardless of character case (upper case or lower case hash is the same).
921         return hash * HASH_CODE_C1 +
922                 // Low order int
923                 hashCodeAsciiSanitize((int) value) * HASH_CODE_C2 +
924                 // High order int
925                 (int) ((value & 0x1f1f1f1f00000000L) >>> 32);
926     }
927 
928     static int hashCodeAsciiSanitize(int value) {
929         return value & 0x1f1f1f1f;
930     }
931 
932     static int hashCodeAsciiSanitize(short value) {
933         return value & 0x1f1f;
934     }
935 
936     static int hashCodeAsciiSanitize(byte value) {
937         return value & 0x1f;
938     }
939 
940     static ClassLoader getClassLoader(final Class<?> clazz) {
941         if (System.getSecurityManager() == null) {
942             return clazz.getClassLoader();
943         } else {
944             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
945                 @Override
946                 public ClassLoader run() {
947                     return clazz.getClassLoader();
948                 }
949             });
950         }
951     }
952 
953     static ClassLoader getContextClassLoader() {
954         if (System.getSecurityManager() == null) {
955             return Thread.currentThread().getContextClassLoader();
956         } else {
957             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
958                 @Override
959                 public ClassLoader run() {
960                     return Thread.currentThread().getContextClassLoader();
961                 }
962             });
963         }
964     }
965 
966     static ClassLoader getSystemClassLoader() {
967         if (System.getSecurityManager() == null) {
968             return ClassLoader.getSystemClassLoader();
969         } else {
970             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
971                 @Override
972                 public ClassLoader run() {
973                     return ClassLoader.getSystemClassLoader();
974                 }
975             });
976         }
977     }
978 
979     static int addressSize() {
980         return UNSAFE.addressSize();
981     }
982 
983     static long allocateMemory(long size) {
984         return UNSAFE.allocateMemory(size);
985     }
986 
987     static void freeMemory(long address) {
988         UNSAFE.freeMemory(address);
989     }
990 
991     static long reallocateMemory(long address, long newSize) {
992         return UNSAFE.reallocateMemory(address, newSize);
993     }
994 
995     static boolean isAndroid() {
996         return IS_ANDROID;
997     }
998 
999     private static boolean isAndroid0() {
1000         // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android.
1001         // Rather than check if certain classes are present, just check the VM, which is tied to the JDK.
1002 
1003         // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of Android, the
1004         // OpenJDK is used, which means `Unsafe` will actually work as expected.
1005 
1006         // Android sets this property to Dalvik, regardless of whether it actually is.
1007         String vmName = SystemPropertyUtil.get("java.vm.name");
1008         boolean isAndroid = "Dalvik".equals(vmName);
1009         if (isAndroid) {
1010             logger.debug("Platform: Android");
1011         }
1012         return isAndroid;
1013     }
1014 
1015     private static boolean explicitTryReflectionSetAccessible0() {
1016         // we disable reflective access
1017         return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible",
1018                 javaVersion() < 9 || RUNNING_IN_NATIVE_IMAGE);
1019     }
1020 
1021     static boolean isExplicitTryReflectionSetAccessible() {
1022         return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
1023     }
1024 
1025     static int javaVersion() {
1026         return JAVA_VERSION;
1027     }
1028 
1029     private static int javaVersion0() {
1030         final int majorVersion;
1031 
1032         if (isAndroid0()) {
1033             majorVersion = 6;
1034         } else {
1035             majorVersion = majorVersionFromJavaSpecificationVersion();
1036         }
1037 
1038         logger.debug("Java version: {}", majorVersion);
1039 
1040         return majorVersion;
1041     }
1042 
1043     // Package-private for testing only
1044     static int majorVersionFromJavaSpecificationVersion() {
1045         return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
1046     }
1047 
1048     // Package-private for testing only
1049     static int majorVersion(final String javaSpecVersion) {
1050         final String[] components = javaSpecVersion.split("\\.");
1051         final int[] version = new int[components.length];
1052         for (int i = 0; i < components.length; i++) {
1053             version[i] = Integer.parseInt(components[i]);
1054         }
1055 
1056         if (version[0] == 1) {
1057             assert version[1] >= 6;
1058             return version[1];
1059         } else {
1060             return version[0];
1061         }
1062     }
1063 
1064     private PlatformDependent0() {
1065     }
1066 }