學習 Android(十五)NDK進階及性能優化

學習 Android(十五)NDK進階及性能優化

對 NDK 相關知識有了初步的了解之后,我們可以更加深入的去學習 NDK 相關知識,接下來,我們將按照以下步驟進行深入學習:

  • 深入理解JNI調用過程和性能消耗
  • 常見 JNI 坑(比如頻繁創建Java對象、內存泄漏)
  • 掌握 Native 內存管理,避免泄漏和崩潰
  • 學習 pthread 多線程和同步機制,和 Android 線程的配合
  • 多線程環境下調用 JNI 注意事項,跨線程回調技巧

1. 深入理解 JNI 調用過程和性能消耗

深入理解 JNI 調用過程和性能消耗,是掌握 Android NDK 開發的關鍵,有助于寫出高效、穩定的混合代碼。

1.1 JNI 調用過程詳解

JNI(Java Native Interface) 是 Java虛擬機(JVM)與本地(Native)代碼交互的橋梁,Android 上是 JVM 的子集 ———— ART(Android Runtime)。JNI 允許 Java 調用 C/C++,也允許 Native 調用 Java 方法。

1.1.1 Java 調用 Native 的典型流程
  • 聲明 Native 方法

    Java 代碼通過 native 關鍵字聲明本地方法,并加載 native 庫:

    class MyClass {static {System.loadLibrary("myLib");}public native int nativeMethod(int arg);
    }
    
  • Native 端實現(C/C++)

    使用 JNI 約定的函數簽名實現:

    extern "C" JNIEXPORT jint JNICALL
    Java_com_example_MyClass_nativeMethod(JNIEnv* env, jobject thiz, jint arg) {
    }
    
  • 調用流程

    • Java 層調用 native 方法時,ART 會查找與方法簽名匹配的本地實現

    • 通過 JNI 函數指針跳轉到本地實現

    • 本地代碼通過傳入的 JNIEnv* 環境指針訪問 JVM 提供的接口(操作對象、數組、調用 Java 方法)

    • 執行完畢返回結果,JNI 自動轉換回 Java 層

1.1.2 Native 回調 Java
  • 本地代碼通過 JNIEnv 指針調用 CallVoidMethodCallIntMethod 等 JNI 函數,訪問 Java 對象。

  • 使用 FindClass 查找 Java 類,GetMethodID 獲取方法 ID 等。

1.1.3 JNIEnv 和線程關聯
  • 每個線程都必須有自己的 JNIEnv* 指針,不能跨線程使用。

  • Java 線程進入 Native 代碼時,Java 虛擬機會傳入 JNIEnv

  • Native 線程調用 Java 方法前,必須附加到 JVM(AttachCurrentThred)獲取 JNIEnv


1.2 JNI 調用的性能消耗來源分析

雖然 JNI 是實現 Java 和 C/C++ 互操作的唯一通道,但調用代價較高,性能損耗主要來自以下方面:

1.2.1 調用開銷

每次 Java 調用 Native 方法,都涉及 JNI 橋接、參數轉換、堆棧切換等,成為跨語言調用開銷。

  • 方法查找

    使用 GetMethodIDFindClass 等接口查找類/方法都會引發字符串查找和反射操作,建議提前緩存 ID。

  • 參數轉換

    JNI 參數和返回值往往需要進行轉換,比如 Java 數組轉 native 數組(GetIntArrayElements),這會產生內存拷貝和映射。

  • 堆棧切換

    從 Java 虛擬機切換到 native 運行環境,也涉及上下文切換開銷。

1.2.2 頻繁調用和跨界面層傳遞大量數據
  • 若調用 JNI 設計不合理,頻繁調用小粒度函數,開銷累計顯著。

  • 大量數據傳遞(如大數組、復雜對象)通過 JNI 參數傳輸,會產生內存復制,影響性能。

1.2.3 內部管理和局部引用開銷
  • JNI 會在 native 層為 Java 對象創建局部引用,如果不及時釋放會導致局部引用表溢出。

  • 使用 NewGlobalRef 增加全局引用也帶來額外管理成本。

1.2.4 異常檢測

每次 JNI 調用之后,JNI 環境會檢測是否有 Java 異常,需要額外執行異常處理流程,若異常頻發也影響性能。


1.3 JNI 性能優化使用技巧

1.3.1 減少 JNI 調用次數
  • 設計合理的接口,盡量減少 Java 和 Native 之間的頻繁小函數調用,更傾向于批量調用。

  • 把一些需要循環調用的邏輯放到 Native 層一次處理完。

1.3.2 緩存方法ID和類引用
  • 緩存 jclass 和方法IDjmethodId,避免頻繁使用 FindClassGetMethodID

  • 注意緩存的類引用要全局引用(NewGlobalRef),避免被 GC 回收。

1.3.3 優化數組和字符串操作
  • 對數組,優先使用 GetPrimitiveArrayCritical ,減少復制(但要注意對代碼穩定性和互斥性的影響)。

  • 傳遞大數組時,盡量避免復制,改為操作指針/緩沖區。

  • 對于 String 類型,避免頻繁轉換,盡量在 native 一側使用 UTF-8 編碼(GetStringUTFChars)。

1.3.4 縮短本地代碼運行時間/減少局部引用
  • 本地代碼不要做耗時操作后立刻回到 Java,減少跨界調用壓。

  • 使用DeleteLocalRef顯式釋放局部引用,防止泄漏;對于大循環內產生大量局部引用更要注意。

1.3.5 線程相關優化
  • 避免頻繁調用AttachCurrentThreadDetachCurrentThread,一般線程周期內只調用一次。
1.3.6 異常判斷與處理要有選擇性
  • JNI 異常檢測開銷不算太大,但頻繁觸發異常檢查會影響性能。
  • 合理判斷并只在需要時檢查異常,如無異常預期場景可優化。

2. 常見JNI坑(比如頻繁創建Java對象、內存泄漏)

在 Android NDK 開發中,JNI 是 Java 與 Native 代碼交互的橋梁,但不當使用很容易出現問題,導致性能問題、內存泄漏甚至程序崩潰。接下來我們分析一些常見的 JNI 坑,尤其是頻繁創建 Java 對象、內存泄漏,并研究如何規避。

2.1 頻繁創建 Java 對象的坑

2.1.1 現象與原因
  • JNI 代碼中頻繁通過 NewObjectNewStringUTFNewObjectArray 等接口創建 Java 對象,尤其是在循環內。

  • 這會導致:

    • JVM 頻繁進行對象分配和 GC,嚴重影響性能。

    • 由于所有新建對象均為局部引用,未及時釋放可能導致局部引用表溢出

2.1.2 典型示例
for (int i = 0; i < n; i++) {jstring str = env.NewStringUTF("hello");// 使用 str// 如果這里不調用 DeleteLocalRef, str 累積導致局部引用溢出
}
2.1.3 解決方案
  • 避免在循環中頻繁創建 Java 對象,盡量批量創建或復用。

  • 及時釋放局部引用

    JNI 代碼中局部引用默認在函數返回時釋放,但對于長時間運行的循環應手動調用:

    env.DeleteLocalRef(str);
    
  • 如果對象只在 Native 層使用,盡量用 Native 數據結構存儲,減少Java對象轉換

  • 使用全局引用緩存對象,但需注意手動釋放,以避免全局內存泄漏。


2.2 內存泄漏問題

JNI 內存泄漏主要有兩大來源:

2.2.1 局部引用不釋放導致局部引用表溢出
  • 每個 JNI 本地方法有一個局部引用表,容量有限(一般512個引用)。
  • 如果 JNI 方法創建或獲取大量局部引用,但不及時釋放,且方法運行時間較長,局部引用表會溢出,導致崩潰。

解決方法:

  • 盡量縮短本地方法運行時長,分批處理任務。
  • 循環內顯式調用?DeleteLocalRef?釋放局部引用。
  • 對大批量 Java 對象操作時,使用PushLocalFrame?和?PopLocalFrame?管理局部引用。

示例:

for (int i = 0; i < bigNum; i++) {jstring str = env.NewStringUTF("test");// 業務邏輯env.DeleteLocalRef(str);
}
2.2.2 全局引用未釋放導致全局內存泄漏
  • 使用?NewGlobalRef?創建的全局引用不會被 GC 自動回收。
  • 如果程序中全局引用被創建后沒有被釋放,導致內存泄漏。

解決方法:

  • 對不再使用的全局引用調用?DeleteGlobalRef?釋放。

示例

jobject globalObj = env.NewGlobalRef(localObj);
// 業務使用
env.DeleteGlobalRef(globalObj);
2.2.3 字符串和數組 Get/Release 不匹配

JNI中很多接口都需要用戶主動釋放資源,如:

  • GetStringUTFChars?與?ReleaseStringUTFChars
  • GetIntArrayElements?與?ReleaseIntArrayElements

如果不調用釋放接口,可能會導致內存泄漏或者數據未同步。

示例:

const char* nativeStr = env.GetStringUTFChars(jstr, 0);
// 使用 nativeStr,但忘了調用釋放
// env.ReleaseStringUTFChars(jstr, nativeStr);

3. 掌握 Native 內存管理,避免泄漏和崩潰

在 Android NDK 及其他使用 C/C++ 開發的 Native 代碼中,內存管理是開發穩定、高效應用的根本技能。相比 Java,Native 代碼需要開發者手動管理內存,一旦失誤可能導致內存泄漏、野指針、崩潰等嚴重問題。接下來我們進行全面理解和掌握 Native 內存管理,避免內存相關的坑。

3.1 內存泄漏的根本原因與規避策略

場景描述避免策略
未釋放 malloc/new 的內存使用 malloc/new 分配后未 free/delete采用智能指針(C++)或顯式成對調用;如 unique_ptr
分配的對象被提前返回/異常中斷出現 early return 或異常路徑,未釋放使用 RAII 模式自動釋放資源
JNI New* 函數未 Delete*創建局部/全局引用后未釋放使用 DeleteLocalRef / DeleteGlobalRef
多線程共享對象未同步釋放多線程訪問同一對象導致重復釋放/未釋放加鎖保護共享資源,避免野指針

3.2 JNI 資源管理核心規則

3.2.1 GetStringUTFChars / ReleaseStringUTFChars
  • 這兩個 API 不會復制 Java 字符串內存,而是返回指針(有時會)。

    什么叫做有時會呢?

    關于 GetStringUTFChars 是否復制 Java 字符串內存的問題,確實存在「有時會,有時不會」的情況,這是由 JVM 的實現細節字符串內容 共同決定的。

    先看官方文檔定義

    const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);

    • 返回一個指向 UTF-8 編碼字符串的指針。

    • *isCopy 會被設置為:

      • JNI_TRUE:表示 JVM 復制了一份內存

      • JNI_FALSE:表示返回的是 JVM 內部的只讀緩存指針不是拷貝)。

    什么時候會復制?

場景解釋
Java 字符串包含 非 ASCII 字符JVM 需要將 UTF-16 編碼轉換為 UTF-8
JVM 無法保證返回區域是連續內存比如字符串被壓縮存儲時
字符串內容被壓縮/混淆存儲JVM 無法零拷貝轉換
特定 JVM 實現本身策略就是安全第一比如 Android ART 通常直接復制
使用多線程共享字符串訪問JVM 會返回副本保證線程安全

什么時候不會復制?

場景解釋
字符串內容是 ASCII,且結構簡單無需轉換,JVM 可以提供只讀指針
使用的是 HotSpot VM,且 JDK 版本較低在某些平臺上,HotSpot 優化路徑中可能避免復制
單線程訪問,JVM 優化已緩存字符串JVM 內部可能已有 UTF-8 緩存區
  • 用完后必須 ReleaseStringUTFChars,否則會占用 JVM 內部緩存區。

    為什么必須 ReleaseStringUTFChars

    即使 JVM 沒有復制,也要調用 ReleaseStringUTFChars,因為:

    1. 你不知道是否復制了(依賴運行時行為);

    2. JVM 可能會在你釋放之前鎖定該字符串區域

    3. 不釋放可能導致 內存泄漏阻塞 JVM 垃圾回收

    4. 有些 JVM 會記錄這個指針的使用情況,未釋放可能造成崩潰。

3.2.2 NewLocalRefDeleteLocalRef
  • JNI 局部引用存在于調試棧幀中,方法退出自動釋放

  • 若創建大量局部引用,應主動 DeleteLocalRef,避免 Local reference table overflow

3.2.3 全局引用需手動釋放
jobject g_obj = (*env)->NewGlobalRef(env, obj);
// ...使用
(*env)->DeleteGlobalRef(env, g_obj);

3.3 調試與診斷工具

工具用途
Valgrind(Native)檢查 C/C++ 內存泄漏、越界訪問
ASan(AddressSanitizer)更適合 Android NDK,用于 native 崩潰和越界
Perfetto / systrace查找 native 層卡頓和資源濫用
Android Studio Profiler追蹤 JNI 調用和內存泄漏情況
logcat 日志分析搭配 __android_log_print 分析生命周期

3.4 防止崩潰的工程實踐

問題防范措施
空指針解引用嚴格檢查 null,使用智能指針封裝訪問
野指針/重復釋放避免裸指針,釋放后設置為 nullptr
多線程并發訪問線程同步+生命周期管理
Java 調用 native 后釋放對象使用全局引用保護生命周期,或采用 WeakGlobalRef

4. 學習 pthread 多線程和同步機制,和 Android 線程的配合

pthread 在 NDK 中是繞不開的核心技術,接下來我們來快速的學習和了解 pthread多線程和同步機制,并且如何和 Android 線程的配合

4.1 pthread 在 Android 中的使用

Android 的 Native 層(C/C++)并不支持 Java 的 Thread ,因此如果需要多線程,就用 POSIX 線程庫(pthread),它在 Android SDK 中完全可用:

  • pthread_create:創建線程

  • pthread_join:等待線程結束

  • pthread_mutex_t:互斥鎖

  • pthread_cond_t:條件變量

  • pthread_rwlock_t:讀寫鎖

  • pthread_once:單次初始化

在 Android 上,pthread 的 ABI 與 Linux 一樣,因為 Android 本質也是基于 Linux 內核。


4.2 Android JAVA 線程與 pthread 的配合

Java 線程Native 線程 之間是可以共存的,但要注意幾點:

  • Java 層啟動的線程:如果在 Native 中執行,需要從 JNIEnv 傳入,或者通過 AttachCurrentThread 重新附著(因為每個線程都有自己唯一的 JNIEnv)。

  • Native 啟動的線程:用 pthread_create,如果需要調用 Java 方法,同樣必須先 AttachCurrentThread,否則會崩潰。

示例

void* thread_func(void* arg) {JNIEnv* env;JavaVM* javaVm = (JavaVM*)arg;javaVm->AttachCurrentThread(&env, nullptr);// 這里就可以用 env 調用 Java 方法// ...javaVm->DetachCurrentThread();return nullptr;
}

這段代碼是一個典型的在 Native 線程中通過 JavaVM 獲取 JNIEnv 并調用 Java 方法的示例。我來分析一下關鍵點:

  • 函數原型:

    • 這是一個標準的 POSIX 線程函數,返回 void,接收 void 參數

    • 參數 arg 被強制轉換為 JavaVM* 指針

  • 關鍵操作:

    • AttachCurrentThread():將當前 native 線程附加到 JVM,獲取 JNIEnv 指針

    • DetachCurrentThread():線程結束時解除與 JVM 的關聯

  • 重要細節:

    • 每個線程都需要通過 AttachCurrentThread 獲取自己的 JNIEnv,不能跨線程使用

    • 必須成對調用 Attach/Detach,否則會導致內存泄漏

    • 在 Android 上,不 Detach 會導致 app 崩潰(DEBUG 模式下)

  • 使用場景:

    • 當在非 Java 創建的線程(如 pthread)中需要調用 Java 方法時

    • 常見于 Native 異步回調到 Java 層的場景


4.3 常用的同步原語

同步方式說明
pthread_mutex_t最常用的互斥鎖
pthread_cond_t條件變量
pthread_rwlock_t讀寫鎖
pthread_spinlock_t自旋鎖
pthread_barrier_t屏障(同步多個線程)

在 Android Native 開發里,最常用的依然是互斥鎖 + 條件變量。舉個常見場景:

  • 一個生產者線程寫數據

  • 一個消費者線程讀取數據

  • 通過 pthread_mutex_tpthread_cond_t 同步


4.4 與 Java 層線程的區別

  • Java 的 Thread 實際上由 Android Runtime (ART) 或 Dalvik 管理
    pthread 實現,但對你透明

  • Java 線程有 Looper / Handler / MessageQueue 等機制
    Native 沒有這些機制,需要你手動管理隊列 + 鎖


4.5 Android 中最佳實踐

  • 避免在 Native 層大量啟動線程,因為調試復雜

  • 如果需要高并發,優先考慮 Java 層線程池

  • 在確實需要硬件交互、實時音視頻等高性能 Native 線程時,用 pthread
    并且記得:

    • AttachCurrentThread

    • 正確釋放 DetachCurrentThread

    • JNIEnv 只能在當前線程使用


5. 多線程環境下調用 JNI 注意事項,跨線程回調技巧

在多線程環境下使用 JNI(Java Native Interface)時,必須非常小心,否則會導致 崩潰、內存泄漏、線程掛起 等嚴重問題。以下是實戰經驗總結與跨線程安全調用 Java 的技巧。

5.1 JNI 多線程環境下的基本準則

5.1.1 JNIEnv* 是線程私有的
  • 每個線程都必須使用自己綁定的 JNIEnv*

  • 不能跨線程傳遞 JNIEnv* 指針,否則會崩潰或產生不確定行為

5.1.2 子線程中使用 JNI 必須先附加線程
  • 使用 JavaVM* 中的 AttachCurrentThread()獲取當前線程的中 JNIEnv*

  • 線程退出前必須執行 DetachCurrentThread(),否則 JVM 會泄漏線程資源

5.2 JNI 跨線程回調 Java 的正確方式

場景:Native 中開啟一個線程,任務完成后回調 Java 的方法

步驟:

  1. 緩存 JavaVM* 和 Java 層對象的 jobject(用 NewGlobalRef() 防止被 GC)

  2. 在 Native 線程中通過 AttachCurrentThread() 獲取 JNIEnv*

  3. 調用 Java 方法(例如回調)

  4. 調用完畢后 DetachCurrentThread()

示例代碼:

Kotlin / Java

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)startNativeTask()}external fun startNativeTask()companion object {// Used to load the 'hello' library on application startup.init {System.loadLibrary("hello")}}fun onNativeTaskComplete() {runOnUiThread {Toast.makeText(this, "任務完成", Toast.LENGTH_SHORT).show()}}}

Native


JavaVM *g_vm = nullptr;
std::atomic<jobject> g_callback_obj{nullptr};jint JNI_OnLoad(JavaVM *vm, void *) {g_vm = vm;return JNI_VERSION_1_6;
}void* thread_func(void*) {JNIEnv* env;if (g_vm->AttachCurrentThread(&env, nullptr) != JNI_OK) {__android_log_print(ANDROID_LOG_ERROR, "NativeThread", "Attach failed");return nullptr;}if (g_callback_obj == nullptr) {__android_log_print(ANDROID_LOG_ERROR, "NativeThread", "Callback object is null");g_vm->DetachCurrentThread();return nullptr;}jclass cls = env->GetObjectClass(g_callback_obj);if (cls == nullptr) {__android_log_print(ANDROID_LOG_ERROR, "NativeThread", "Class not found");g_vm->DetachCurrentThread();return nullptr;}jmethodID methodID = env->GetMethodID(cls, "onNativeTaskComplete", "()V");if (methodID == nullptr) {__android_log_print(ANDROID_LOG_ERROR, "NativeThread", "Method not found");env->DeleteLocalRef(cls);g_vm->DetachCurrentThread();return nullptr;}env->CallVoidMethod(g_callback_obj, methodID);if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();}env->DeleteLocalRef(cls);g_vm->DetachCurrentThread();return nullptr;
}extern "C"
JNIEXPORT void JNICALL
Java_com_example_hello_MainActivity_startNativeTask(JNIEnv *env, jobject thiz) {jobject old_ref = g_callback_obj.exchange(env->NewGlobalRef(thiz));if (old_ref != nullptr) {env->DeleteGlobalRef(old_ref);}pthread_t thread;pthread_create(&thread, nullptr, thread_func, nullptr);pthread_detach(thread); // 避免內存泄漏
}

示例代碼分析:

關鍵組件解析:

變量/函數作用
g_vm全局?JavaVM*,用于跨線程 Attach/Detach JNIEnv
g_callback_obj全局引用(jobject),保存 Java 層的回調對象(MainActivity?實例)
JNI_OnLoad動態庫加載時初始化?g_vm
thread_funcNative 線程函數,執行任務并回調 Java 方法
startNativeTaskJNI 入口,啟動 Native 線程并設置回調對象

內存管理分析:

  1. 全局引用 (g_callback_obj)

    • 正確做法:

      • 使用?env->NewGlobalRef()?將局部引用提升為全局引用(避免被 GC 回收)。

      • 每次更新回調對象時,先刪除舊引用(DeleteGlobalRef)。

    • 代碼驗證:

      jobject old_ref = g_callback_obj.exchange(env->NewGlobalRef(thiz));
      if (old_ref != nullptr) {env->DeleteGlobalRef(old_ref);  // 釋放舊引用
      }
      

      優點:避免了全局引用泄漏。

  2. 局部引用(cls)

    • 正確做法:

      • GetObjectClass?返回的?jclass?是局部引用,需手動釋放(DeleteLocalRef)。

      • 代碼中在?DetachCurrentThread?前正確釋放:

        env->DeleteLocalRef(cls);
        

線程安全設計

  1. g_callback_obj?的原子操作
  • 問題:多線程可能同時讀寫?g_callback_obj

  • 解決方案

    • 使用?std::atomic<jobject>?確保原子性。

    • 通過?exchange?方法安全更新引用:

      jobject old_ref = g_callback_obj.exchange(env->NewGlobalRef(thiz));
      
  1. JNIEnv 的線程隔離
  • 規則JNIEnv*?是線程局部的,不能跨線程共享。

  • 代碼驗證

    • 每個線程通過?AttachCurrentThread?獲取自己的?env

    • 線程退出前調用?DetachCurrentThread(即使在異常情況下也通過?try-catch?保證執行)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/92234.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/92234.shtml
英文地址,請注明出處:http://en.pswp.cn/web/92234.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

QT5.12.8 QTabWidget 透明樣式QSS

/* 設置QTabWidget本身 :不加也行*/ QTabWidget#aaa_tabwdt {background: transparent;border: none; /* 移除邊框可能有助于透明效果 */ }/* 標簽頁內的容器部件 :必須加&#xff0c;標簽也才會透明 */ QTabWidget#aaa_tabwdt QWidget, QTabWidget#aaa_tabwdt QFrame {backgro…

【FAQ】Script導出SharePoint 目錄文件列表并統計大小

一、只導出文件列表的方法 1) 保存腳本&#xff08;建議名&#xff1a;D:\tmp\Export-SharePoint-FileList.ps1&#xff09; <# 導出 SharePoint 指定文件夾&#xff08;含子文件夾&#xff09;的文件列表到 CSV&#xff08;不統計大小&#xff09; 前提&#xff1a;已安…

《Thinking in Java》讀書筆記---控制執行流程

就像有感知的生物一樣&#xff0c;程序必須在執行過程中控制它的世界&#xff0c;并做出選擇。在Java中&#xff0c;你要使用執行控制語句來作出選擇。一、流程控制基礎概念1.1 流程控制的重要性流程控制結構決定了程序執行的順序和邏輯分支&#xff0c;是編程語言中最基礎也是…

極驗 G-star 人才特訓營:為業務安全領域培養下一代新興力量

本文導讀 極驗為什么要啟動 G-star 實習生培養計劃&#xff1f;50多位來自多所高校的同學&#xff0c;在極驗經歷了一場怎樣的“非典型”實習&#xff1f;技術大咖親授&#xff0c;先培訓再實戰&#xff0c;極驗打造的是怎樣的人才體系&#xff1f;同學有話說&#xff1a;培養計…

攻防世界-web-csaw-mfw

一.題目分析這邊提示使用了Git&#xff0c;試著訪問.git看是否存在.git泄露瀏覽了一下&#xff0c;很多都是亂碼&#xff0c;想著用githack將git庫克隆下看一下二.操作python2 GitHack.py http://url/.git訪問了一下flag.php&#xff0c;沒啥發現&#xff0c;在看一下index.php…

202506 電子學會青少年等級考試機器人四級實際操作真題

更多內容和歷年真題請查看網站&#xff1a;【試卷中心 -----> 電子學會 ----> 機器人技術 ----> 四級】 網站鏈接 青少年軟件編程歷年真題模擬題實時更新 2025年6月 青少年等級考試機器人實操真題四級 實際操作 主題&#xff1a;感應節能燈&#xff08;四級&am…

DLT645電表數據 保存到MySQL數據庫項目案例

目錄 1 案例說明 2 VFBOX網關工作原理 3 準備工作 4 配置VFBOX網關采集DLT645電表數據 5 網關寫數據到MYSQL數據庫 6 安裝MYSQL數據庫 7 其他說明 8 案例總結 1 案例說明 設置網關采集DLT645電表數據數據把采集的數據保存到MySQL數據庫。 2 VFBOX網關工作原理 VFBOX網關…

Redux與React - 異步狀態操作(React快速上手4)

異步操作樣板代碼1. 創建store的寫法保持不變&#xff0c;配置好同步修改狀態的方法 2. 單獨封裝一個函數&#xff0c;在函數內部return一個新函數&#xff0c;在新函數中 2.1 封裝異步請求獲取數據 2.2 調用同步actionCreater傳入異步數據生成一個action對象&#xff0c;并使用…

win10桌面右鍵沒有新建word

win10右鍵新建word不見解決方法1、點擊開始&#xff0c;找到運行命令行&#xff0c;輸入regedit&#xff0c;打開注冊表。2、在左側找到HKEY_CLASSES_ROOT目錄&#xff0c;并展開。3.找到.docx 雙擊&#xff08;默認&#xff09;一項&#xff0c;將其改為 Word.Document.12。關…

Docker國內可用鏡像(2025.08.06測試)

Docker渡渡鳥鏡像可用&#xff08;測試時間2025.08.06&#xff09;https://docker.aityp.com/使用渡渡鳥鏡像pull ollama latest 例子&#xff1a;docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/ollama/ollama:0.10.1毫秒鏡像和軒轅鏡像也可用&#xff0c;但…

決策樹的實際案例

決策樹作為一種直觀、易解釋的機器學習算法&#xff0c;在金融、醫療、電商、風控等多個領域都有廣泛的實際應用。以下將講解1個典型案例&#xff1a;貸款違約預測。對于貸款違約預測&#xff0c;即在貸款人員在貸款之前對其進行預測&#xff0c;通過他的眾多特征情況判別是否可…

bool 類型轉換運算符重載

以下是一個極簡且聚焦核心知識點的示例代碼&#xff0c;用最直觀的方式演示 bool 類型轉換運算符重載的觸發邏輯、使用場景和避坑點&#xff0c;幫你快速掌握&#xff1a;cpp運行#include <iostream> using namespace std;// 核心類&#xff1a;演示 bool 轉換運算符 cla…

LVGL代碼框架簡介

LVGL代碼框架介紹LVGL&#xff08;Light and Versatile Graphics Library&#xff09;是一個輕量級、功能強大的嵌入式圖形庫。其代碼架構設計清晰&#xff0c;模塊化程度高。1. 整體架構層次LVGL采用分層架構設計&#xff0c;主要包含以下幾個層次&#xff1a;┌───────…

【云計算】云主機的親和性策略(三):云主機 宿主機

《云主機的親和性策略》系列&#xff0c;共包含以下文章&#xff1a; 1?? 云主機的親和性策略&#xff08;一&#xff09;&#xff1a;快樂旅行團2?? 云主機的親和性策略&#xff08;二&#xff09;&#xff1a;集群節點組3?? 云主機的親和性策略&#xff08;三&#xf…

【世紀龍科技】虛擬技術助力職教汽車自動變速器拆裝虛擬實訓軟件

在職業院校汽車專業實訓課堂上&#xff0c;教師常面臨這樣的兩難&#xff1a;學生圍在昂貴的自動變速器實物旁&#xff0c;卻因設備數量有限只能輪流操作&#xff1b;拆裝步驟稍有偏差便可能損壞精密部件&#xff0c;維修成本讓本就緊張的教學經費雪上加霜&#xff1b;傳統教學…

[LVGL] 配置lv_conf.h | 條件編譯 | 顯示屏lv_display

鏈接&#xff1a;https://docs.lvgl.io/master/ docs&#xff1a;LVGL LVGL&#xff08;Light and Versatile Graphics Library&#xff09;是用于在資源受限的嵌入式系統上創建圖形用戶界面&#xff08;GUI&#xff09;的開源解決方案。 它提供豐富的控件和靈活的**事件系…

sqli-labs通關筆記-第32關 GET寬字符注入(單引號閉合 手工注入+腳本注入兩種方法)

目錄 一、寬字符注入 二、代碼審計 1、代碼審計 2、SQL注入安全性分析 三、滲透實戰 1、進入靶場 2、id1探測 3、id%df-- 探測 4、手工注入 &#xff08;1&#xff09;獲取列數 &#xff08;2&#xff09;獲取回顯位 &#xff08;3&#xff09;獲取數據庫名 &…

MySQL的創建管理表:

目錄 基礎知識&#xff1a; 一條數據存儲的過程&#xff1a; 標識符命名規則&#xff1a; 創建和管理數據庫&#xff1a; 創建數據庫&#xff1a; 創建數據庫&#xff1a; 創建數據庫并指定字符集&#xff1a; 判斷數據庫是否存在&#xff0c;不存在則創建數據庫&#…

Linux Vi常用指令總結

Vi&#xff08;及其改進版 Vim&#xff09;是 Linux 中常用的文本編輯器&#xff0c;雖然入門有一定門檻&#xff0c;但熟練掌握后效率極高。以下是常見指令分類整理&#xff1a;1. 模式切換 普通模式&#xff08;命令模式&#xff09;&#xff1a;啟動后的默認模式&#xff0c…

解決遠程連接云服務器mysql編號1130問題

連接云服務器mysql失敗&#xff0c;可能是因為ip發生改變&#xff0c;被mysql攔截。我自己前后做了兩件事。大家趕時間可直接從二開始&#xff0c;不放心的就從一開始到結束一、在云服務器實例中為當前ip配置安全組權限。1.找到當前服務器的實例&#xff0c;進入安全組。為其增…