日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
談?wù)刄nsafe在Java中的作用

前言

最近在 Kotlin 項(xiàng)目中發(fā)現(xiàn),定義的 data class?(成員變量都聲明不可空)經(jīng)過在 Gson? 解析后,可以得到成員變量為空的對(duì)象,而不是得到解析失敗,那么就很容易造成后續(xù)代碼的非預(yù)期運(yùn)行,因?yàn)槌蓡T變量都按不可空的情況來處理,最終喜提 NullPointerException。

分析原因?

在 Gson? 的代碼中找到實(shí)例化對(duì)象的地方,經(jīng)過幾種構(gòu)造方式失敗后最終會(huì)使用 Unsafe 的來構(gòu)造實(shí)例。

/**
* Returns a function that can construct an instance of a requested type.
*/
public final class ConstructorConstructor {
...
public ObjectConstructor get(TypeToken typeToken) {
final Type type = typeToken.getType();
final Class rawType = typeToken.getRawType();
...
ObjectConstructor defaultConstructor = newDefaultConstructor(rawType);
if (defaultConstructor != null) {
return defaultConstructor;
}
...
// finally try unsafe
return newUnsafeAllocator(type, rawType);
}
...
}

Unsafe 是位于 ??sun.misc?? 包下的一個(gè)類,主要提供一些用于執(zhí)行低級(jí)別、不安全操作的方法,如直接訪問系統(tǒng)內(nèi)存資源、自主管理內(nèi)存資源等,這些方法在提升 Java 運(yùn)行效率、增強(qiáng) Java 語言底層資源操作能力方面起到了很大的作用。Unsafe 使 Java 語言擁有了類似 C 語言指針一樣操作內(nèi)存空間的能力,對(duì) Unsafe 的使用一定要慎重。

Gson  采用的便是其對(duì)象操作的能力,使用 ??allocateInstance?? 方法,達(dá)到繞過構(gòu)造方法創(chuàng)建對(duì)象。

try {
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object unsafe = f.get(null);
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public T newInstance(Class c) throws Exception {
assertInstantiable(c);
return (T) allocateInstance.invoke(unsafe, c);
}
};
} catch (Exception ignored) {
}

結(jié)論?

通過 Unsafe#allocateInstance? 實(shí)例化的對(duì)象繞過了構(gòu)造函數(shù),在 Koltin 中要額外注意,因?yàn)?Kotlin 對(duì)非空變量的賦值都會(huì)經(jīng)過  Intrinsics.checkParameterIsNotNull 的處理,而此時(shí)構(gòu)造函數(shù)的一系列判斷均被繞過,導(dǎo)致上下文不一致。

「「為什么要通過反射來獲取 Unsafe?」」

Unsafe 為單例實(shí)現(xiàn),并且 getUnsafe()? 靜態(tài)方法僅在調(diào)用的類為引導(dǎo)類加載器 BootstrapClassLoader 加載時(shí)才合法,直接反射獲取 Unsafe 實(shí)例吧!

public final class Unsafe { 
private static final Unsafe theUnsafe;
...
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
...
}

Unsafe 的其他應(yīng)用?

在 Android P 版本之后 限制隱藏 API 的調(diào)用,作為一個(gè) 「「合格」」 的開發(fā)者應(yīng)該尊重官方的規(guī)則,也有利于項(xiàng)目的長期維護(hù)。但偶爾也要試試打破規(guī)則!

  • 限制隱藏 API 的調(diào)用
    https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces?hl=zh-cn。

「「先聊聊系統(tǒng)如何實(shí)現(xiàn)這個(gè)限制?」」

通常調(diào)用隱藏 API 都是通過反射的方式,但是反射的調(diào)用也被攔截。

源碼分析可以找到 java.lang.Class#getDeclaredMethod()? 最終會(huì)調(diào)用 native 方法 getDeclaredMethodInternal。

static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
jstring name, jobjectArray args) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
Handle result = hs.NewHandle(
mirror::Class::GetDeclaredMethodInternal(
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode(name),
soa.Decode>(args)));
if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference(result.Get());
}

當(dāng) 「「ShouldBlockAccessToMember」」 返回 true 時(shí),那么直接返回 nullptr,上層就會(huì)拋 ??NoSuchMethodXXX?? 異常,觸發(fā)了系統(tǒng)限制。

template
inline Action GetMemberAction(T* member,
Thread* self,
std::function fn_caller_is_trusted,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
// Decode hidden API access flags.
// NB Multiple threads might try to access (and overwrite) these simultaneously,
// causing a race. We only do that if access has not been denied, so the race
// cannot change Java semantics. We should, however, decode the access flags
// once and use it throughout this function, otherwise we may get inconsistent
// results, e.g. print whitelist warnings (b/78327881).
HiddenApiAccessFlags::ApiList api_list = member->GetHiddenApiAccessFlags();
Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
if (action == kAllow) {
// Nothing to do.
return action;
}
// Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
// This can be *very* expensive. Save it for last.
if (fn_caller_is_trusted(self)) {
// Caller is trusted. Exit.
return kAllow;
}
// Member is hidden and caller is not in the platform.
return detail::GetMemberActionImpl(member, api_list, action, access_method);
}

主要判斷邏輯中三個(gè)條件有一處通過就不會(huì)觸發(fā)系統(tǒng)限制,fn_caller_is_trusted 便是判斷調(diào)用者的 Class 是否通過 BootClassLoader 加載,所以系統(tǒng)可以直接調(diào)用隱藏 API,系統(tǒng) Class 均由 BootClassLoader 加載。

通過 BootClassLoader 加載的類,其 ClassLoader 則為 null,那么只要將一個(gè)業(yè)務(wù)中已加載 Class 的 ClassLoader 設(shè)置為 null ,該 Class 便可以通過反射調(diào)用隱藏 API 了。

反射是直接修改 Class.classLoader? 是行不通的,因?yàn)樵撟侄卧谏罨颐麊沃校瑫?huì)拋 NoSuchFiledException。

「「該 Unsafe 登場了」」

通過 Unsafe 拿到 Class 中 classloader 的偏移量,將偏移量處置為 null。

Class 在內(nèi)存中的結(jié)構(gòu)如下,前兩項(xiàng)變量繼承于 Object,分別都是 4 個(gè)字節(jié),所以 classloader 的偏移量為 8。

struct Class {
Class shadow$_klass_;
int shadow$_monitor_;
ClassLoader classLoader;
}

果然偏移量為 8,輸出的是 classloader 信息,設(shè)置為 null,再次 getClassLoader 已經(jīng)變成 BootClassLoader。

class MainActivity : AbsActivity() {
override fun onContentLayoutId(): Int = R.layout.activity_main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val unsafe = UnsafeAndroid()
Timber.d(unsafe.getObject(Reflect::class.java, 8).toString())
unsafe.getAndSetObject(Reflect::class.java, 8, null)
Timber.d("${unsafe.getObject(Reflect::class.java, 8)}")
Timber.d(Reflect::class.java.classLoader.toString())
}
}
D/
(MainActivity.kt:16): dalvik.system.PathClassLoader[DexPathList[[dex file "/data/data/com.x.example/code_cache/.overlay/base.apk/classes2.dex", zip file "/data/app/~~okoSHt8RD79B35SscL93sA==/com.x.example-HuYEILM1Ybt2xxGkn7eViw==/base.apk"],nativeLibraryDirectories=[/data/app/~~okoSHt8RD79B35SscL93sA==/com.x.example-HuYEILM1Ybt2xxGkn7eViw==/lib/arm64, /data/app/~~okoSHt8RD79B35SscL93sA==/com.x.example-HuYEILM1Ybt2xxGkn7eViw==/base.apk!/lib/arm64-v8a, /system/lib64, /system_ext/lib64]]]
D/
(MainActivity.kt:18): null
D/
(MainActivity.kt:19): java.lang.BootClassLoader@42a2eb

本文標(biāo)題:談?wù)刄nsafe在Java中的作用
URL地址:http://www.5511xx.com/article/cojicoo.html