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