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