国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android 性能優(yōu)化系列:崩潰原因及捕獲

這篇具有很好參考價(jià)值的文章主要介紹了Android 性能優(yōu)化系列:崩潰原因及捕獲。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

崩潰的基本原因

拋出異常導(dǎo)致崩潰分析

在日常開發(fā)中崩潰是我們遇到的很常見的情況,可能是 NullPointerException、IllegalArgumentException 等等,當(dāng)應(yīng)用程序拋出這些我們未捕獲的異常時(shí),緊跟著的是應(yīng)用的崩潰,進(jìn)程被殺死并退出。

或許你到現(xiàn)在都一直認(rèn)為是因?yàn)閽伋隽水惓?,所以才會?dǎo)致的進(jìn)程被殺死并退出,認(rèn)為拋出未捕獲的異常就是導(dǎo)致進(jìn)程退出的根本原因。但事實(shí)真的如此嗎?

主線程也是一個線程,所以我們從 Thread 源碼找找是否有什么蹤跡可以解釋:

Thread.java

/**
 * Dispatch an uncaught exception to the handler. This method is
 * intended to be called only by the JVM.
 */
private void dispatchUncaughtException(Throwable e) {
    getUncaughtExceptionHandler().uncaughtException(this, e);
}

public UncaughtExceptionHandler getUncaughtExceptionHandler() {
	// 如果沒有設(shè)置 uncaughtExceptionHandler,由 ThreadGroup group 線程組處理
    return uncaughtExceptionHandler != null ?
        uncaughtExceptionHandler : group;
}

public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
    return defaultUncaughtExceptionHandler;
}

ThreadGroup.java

public void uncaughtException(Thread t, Throwable e) {
    if (parent != null) {
        parent.uncaughtException(t, e);
    } else {
        Thread.UncaughtExceptionHandler ueh =
            Thread.getDefaultUncaughtExceptionHandler();
        if (ueh != null) {
        	// 如果有設(shè)置了 UncaughtExceptionHandler 處理異常捕獲
            ueh.uncaughtException(t, e);
        } else if (!(e instanceof ThreadDeath)) {
            System.err.print("Exception in thread \""
                             + t.getName() + "\" ");
            e.printStackTrace(System.err);
        }
    }
}

上面的源碼是 Thread 處理未捕獲的異常時(shí)會執(zhí)行的代碼,其中 dispatchUncaughtException() 是當(dāng)異常未被捕獲時(shí),它會由 JVM 發(fā)起調(diào)用。

當(dāng)程序代碼運(yùn)行錯誤拋出異常時(shí),如果沒有設(shè)置 Thread.UncaughtExceptionHandler,從 Thread 中并沒有看到有關(guān)進(jìn)程退出的代碼,那為什么我們的程序拋出異常會導(dǎo)致進(jìn)程退出呢?

在 Android 創(chuàng)建 app 進(jìn)程是由 zygote fork 進(jìn)程,而在 zygote 進(jìn)程創(chuàng)建之前則是由 init 進(jìn)程啟動的,每個應(yīng)用程序的入口都是 main() 函數(shù):

RuntimeInit.java

public static void main(String[] argv) {
	...
	commonInit();
	...
}

protected static final void commonInit() {
	...
	// 在所在線程(即主線程)調(diào)用了 Thread.setDefaultUncaughtExceptionHandler
	// 提供 KillApplicationHandler 在應(yīng)用拋出異常時(shí)退出 app 進(jìn)程
    LoggingHandler loggingHandler = new LoggingHandler();
    Thread.setUncaughtExceptionPreHandler(loggingHandler);
    Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler)); 
    ...
}

private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		try {
			...
			ActivityManager.getService().handleApplicationCrash(
					mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
		} catch (Throwable t2) {
			...
		} finally {
			// 捕獲到應(yīng)用程序拋出的異常,最后主動殺死退出進(jìn)程
			Process.killProcess(Process.myPid());
			System.exit(10);
		}
	}
}

可以發(fā)現(xiàn)應(yīng)用程序拋出異常進(jìn)程會退出,實(shí)際上是 RuntimeInit 在主線程設(shè)置了 KillApplicationHandler,捕獲到異常時(shí)主動將應(yīng)用進(jìn)程殺死退出。

所以在這里我們可以得出結(jié)論:拋出未捕獲的異常并不是導(dǎo)致進(jìn)程殺死退出的根本原因而逝導(dǎo)火索,Android 默認(rèn)提供了異常未捕獲時(shí)主動將應(yīng)用進(jìn)程殺死退出的邏輯

簡單總結(jié)下 java crash 未捕獲異常的處理流程:

  • Thread 中遇到未捕獲異常時(shí)會由 JVM 調(diào)用 dispatchUncaughtException()

  • dispatchUncaughtException() 內(nèi)部是運(yùn)用 Thread.UncaughtExceptionHandler 進(jìn)行處理

  • Android 默認(rèn)提供一個 KillApplicationHandler 繼承 Thread.UncaughtExceptionHandler,它提供進(jìn)程退出功能

即我們程序中的代碼拋出未捕獲的異常時(shí),異常會一路往上拋直到由 JVM 處理,JVM 就會調(diào)用 Thread 的 dispatchUncaughtException(),接著就是交給了 Android 在創(chuàng)建應(yīng)用時(shí)設(shè)置的 KillApplicationHandler 將進(jìn)程退出。

AMS 如何承接應(yīng)用的異常信息上報(bào)

在上面分析 RuntimeInit 設(shè)置了 KillApplicationHandler 時(shí),在殺死進(jìn)程前將應(yīng)用崩潰的信息交給了 AMS:

private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		try {
			...
			// AMS 處理進(jìn)程退出前的信息處理
			ActivityManager.getService().handleApplicationCrash(
					mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
		} catch (Throwable t2) {
			...
		} finally {
			Process.killProcess(Process.myPid());
			System.exit(10);
		}
	}
}

那 AMS 在進(jìn)程退出前具體做了哪些事情,接著看源碼:

ActivityManagerService.java

public void handleApplicationCrash(IBinder app,
        ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
    // 拿到進(jìn)程信息
    ProcessRecord r = findAppProcess(app, "Crash");
    final String processName = app == null ? "system_server"
            : (r == null ? "unknown" : r.processName);

    handleApplicationCrashInner("crash", r, processName, crashInfo);
}

// eventType 根據(jù)不同類型記錄 log
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
        ApplicationErrorReport.CrashInfo crashInfo) {
	...
	// java crash、native crash、anr 都會走這里去記錄,通過 eventType 區(qū)分記錄 log
	addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
	...
}

可以發(fā)現(xiàn) AMS 是拿到了應(yīng)用進(jìn)程信息后,最終調(diào)用了 addErrorToDropBox() 將錯誤信息提供給 DropBox。

Android DropBox 是 Android 8 引入的用來持續(xù)化存儲系統(tǒng)數(shù)據(jù)的機(jī)制。主要用于記錄 Android 運(yùn)行過程中,內(nèi)核、系統(tǒng)進(jìn)程、用戶進(jìn)程等出現(xiàn)嚴(yán)重問題時(shí)的 log,可以認(rèn)為這是一個可持續(xù)存儲的系統(tǒng)級別的 logcat。存儲的 log 信息存放在 /data/system/dropbox/(該目錄沒有 root 無法訪問),crash 和 anr 的日志都會存儲在這里。

其中 eventType 參數(shù)表示的是記錄的 log 信息類型,它有三種類型:

  • 未捕捉的 java 異常:crash

  • ANR 異常:anr

  • native 異常:native crash

對于 native crash 系統(tǒng)如何做處理

上面講到了 app 是如何捕捉 java crash,那 native crash 又是如何捕捉的?

ActivityManagerService.java

public void startObservingNativeCrashes() {
	// NativeCrashListener 繼承自 Thread
    final NativeCrashListener ncl = new NativeCrashListener(this);
    ncl.start();
}

而 AMS 的 startObservingNativeCrashes() 是在 SystemServer 創(chuàng)建啟動的時(shí)候調(diào)用:

SystemServer.java

private void startOtherServices() {
	...
	mActivityManagerService.startObservingNativeCrashes();
	...
}

startOtherService() 是在一些核心服務(wù)啟動后才會緊接著調(diào)用的方法用于開啟其他服務(wù),native crash 的監(jiān)控就是在此時(shí)啟動。

接下來看下 native crash 是怎么監(jiān)聽的。

NativeCrashListener.java

// 通信地址,C 層是將數(shù)據(jù)推到這里,java 層也是從這個地址獲取到數(shù)據(jù)
static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";

@Override
public void run() {
	...
	// 這里走的是信號機(jī)制通信,當(dāng)前接收建立管道綁定
	FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
	final UnixSocketAddress sockAddr = UnixSocketAddress.createFileSystem(
			DEBUGGERD_SOCKET_PATH);
	Os.bind(serverFd, sockAddr);
	Os.listen(serverFd, 1);
	Os.chmod(DEBUGGERD_SOCKET_PATH, 0777);
	
	while (true) {
		FileDescriptor peerFd = null;
		try {
			...
			// 掛載等待
			peerFd = Os.accept(serverFd, null /* peerAddress */);
			...
			if (peerFd != null) {
				// 處理 native crash 數(shù)據(jù)
				consumeNativeCrashData(peerFd);
			}
			...
		}
	}
	...
}

void consumeNativeCrashData(FileDescriptor fd) {
	// 根據(jù) fd 拿到數(shù)據(jù)寫到 ByteArrayOutputStream
	...
	final String reportString = new String(os.toByteArray(), "UTF-8");
	// 啟動一個子線程將數(shù)據(jù)結(jié)果給到 AMS 調(diào)用 addErrorToDropBox()
	(new NativeCrashReport(pr, signal, reportString)).start();
}

class NativeCrashReport extends Thread {
	ProcessRecord mApp;
	int mSignal;
	String mCrashReport;

	NativeCrashReport(ProcessRecord app, int signal, String report) {
		super("NativeCrashReport");
		mApp = app;
		mSignal = signal;
		mCrashReport = report;
	}
	
	@Override
	public void run() {
		try {
			// 封裝數(shù)據(jù),將數(shù)據(jù)交給 AMS
            CrashInfo ci = new CrashInfo();
            ci.exceptionClassName = "Native crash";
            ci.exceptionMessage = Os.strsignal(mSignal);
            ci.throwFileName = "unknown";
            ci.throwClassName = "unknown";
            ci.throwMethodName = "unknown";
            ci.stackTrace = mCrashReport;

            if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
            // eventType = native_crash,還是調(diào)用的 AMS.addErrorToDropBox()
            mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
            if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");			
		} catch (Exception e) {
			Slog.e(TAG, "Unable to report native crash", e);
		}
		...
	}
}

通過源碼可以知道,native crash 監(jiān)聽是通過信號量的方式監(jiān)聽一個 fd 文件描述符并掛載等待,當(dāng) fd 返回?cái)?shù)據(jù)時(shí)說明出現(xiàn) native crash,此時(shí)就可以通過 fd 拿到 native crash 的字節(jié)數(shù)據(jù)進(jìn)行封裝,最終還是將數(shù)據(jù)結(jié)果給到 AMS 調(diào)用 addErrorToDropBox(),只是 eventType 傳的是 native_crash。

簡單總結(jié)下 native crash 異常的處理流程:

  • 通信機(jī)制建立,監(jiān)聽一個 fd 文件描述符

  • 接收數(shù)據(jù),掛載等待

  • fd 文件描述符有數(shù)據(jù)返回,處理數(shù)據(jù)

  • 封裝數(shù)據(jù),將結(jié)果給 AMS 調(diào)用 addErrorToDropBox() 寫入存儲信息

系統(tǒng)如何處理 ANR 異常數(shù)據(jù)

因?yàn)檫@里我們只說 ANR 異常最終是如何處理的,所以關(guān)于 ANR 異常發(fā)生的原因和具體流程可以查看 ANR 觸發(fā)原理和分析,這里不再贅述,我們直接定位到 ANR 已經(jīng)發(fā)生的位置:

AppErrors.java

final void appNotResponding(ProcessRecord app, ActivityRecord activity,
        ActivityRecord parent, boolean aboveSystem, final String annotation) {
    // ANR 數(shù)據(jù)處理封裝
	...
	// 這里就是我們常用的 anr 日志存放在 /data/anr 的文件向外輸出了一份
    File tracesFile = ActivityManagerService.dumpStackTraces(
            true, firstPids,
            (isSilentANR) ? null : processCpuTracker,
            (isSilentANR) ? null : lastPids,
            nativePids);
	...
	// 調(diào)用 AMS 的 addErrorToDropBox()
    mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
            cpuInfo, tracesFile, null);	
	...
}

ANR 異常的信息是調(diào)用的 AppErrors 的 appNotResponding(),也是將數(shù)據(jù)處理封裝后給到 AMS 調(diào)用 addErrorToDropBox(),eventType 傳入的是 anr。

簡單總結(jié)下 anr 異常的處理流程:

  • 記錄 anr 對應(yīng)數(shù)據(jù)到 SLOG(framework 日志體系中)

  • 記錄 anr 數(shù)據(jù)到 LOG(主日志體系中)

  • dump 具體數(shù)據(jù)到指定文件中

  • 調(diào)用 AMS 的 addErrorToDropBox() 寫入存儲信息

addErrorToDropBox()

經(jīng)過上面的分析我們知道,無論是 java crash、native crash 還是 anr,最終都會交由 AMS 的 addErrorToDropBox() 處理。那它又是怎么處理的呢?

ActivityManagerService.java

public void addErrorToDropBox(String eventType,
        ProcessRecord process, String processName, ActivityRecord activity,
        ActivityRecord parent, String subject,
        final String report, final File dataFile,
        final ApplicationErrorReport.CrashInfo crashInfo) {
	...
    final StringBuilder sb = new StringBuilder(1024);
    // 添加頭信息
    appendDropBoxProcessHeaders(process, processName, sb);
    // 添加一些頭部 log 信息
    if (process != null) {
        sb.append("Foreground: ")
                .append(process.isInterestingToUserLocked() ? "Yes" : "No")
                .append("\n");
    }
    if (activity != null) {
        sb.append("Activity: ").append(activity.shortComponentName).append("\n");
    }
    if (parent != null && parent.app != null && parent.app.pid != process.pid) {
        sb.append("Parent-Process: ").append(parent.app.processName).append("\n");
    }
    if (parent != null && parent != activity) {
        sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
    }
    if (subject != null) {
        sb.append("Subject: ").append(subject).append("\n");
    }
    sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
    if (Debug.isDebuggerConnected()) {
        sb.append("Debugger: Connected\n");
    }
    sb.append("\n");
	...

	// 讀取對應(yīng)異常數(shù)據(jù)拼裝成字符數(shù)據(jù)
    if (lines > 0) {
        sb.append("\n");

        // Merge several logcat streams, and take the last N lines
        InputStreamReader input = null;
        try {
            java.lang.Process logcat = new ProcessBuilder(
                    "/system/bin/timeout", "-k", "15s", "10s",
                    "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
                    "-b", "main", "-b", "crash", "-t", String.valueOf(lines))
                            .redirectErrorStream(true).start();

            try { logcat.getOutputStream().close(); } catch (IOException e) {}
            try { logcat.getErrorStream().close(); } catch (IOException e) {}
            input = new InputStreamReader(logcat.getInputStream());

            int num;
            char[] buf = new char[8192];
            while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
        } catch (IOException e) {
            Slog.e(TAG, "Error running logcat", e);
        } finally {
            if (input != null) try { input.close(); } catch (IOException e) {}
        }
    }

	// 交給 DropBoxManager 錄入
    dbox.addText(dropboxTag, sb.toString());
    ...
}

DropBoxManager.java

public void addText(String tag, String data) {
    try {
    	// mService 就是 DropBoxManagerService
        mService.add(new Entry(tag, 0, data));
    } catch (RemoteException e) {
        if (e instanceof TransactionTooLargeException
                && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
            Log.e(TAG, "App sent too much data, so it was ignored", e);
            return;
        }
        throw e.rethrowFromSystemServer();
    }
}

當(dāng) AMS 調(diào)用 addErrorToDropBox() 時(shí),它會準(zhǔn)備添加到 DropBoxManager 所需要的頭信息、異常信息的字符數(shù)據(jù),最終交由 DropBoxManager 錄入信息。

DropBoxManager 在 Crash 方案中扮演的角色

DropBox 是 Android 在 API 8 引入的用來持續(xù)化存儲系統(tǒng)數(shù)據(jù)的機(jī)制,主要用于記錄 Android 運(yùn)行過程中,內(nèi)核、系統(tǒng)進(jìn)程、用戶進(jìn)程等出現(xiàn)嚴(yán)重問題時(shí)的 log,可以認(rèn)為 DropBox 就是一個可持續(xù)存儲的系統(tǒng)級別的 logcat。

DropBoxManagerService.java

public DropBoxManagerService(final Context context) {
	// DropBox 信息的存儲日志目錄是 /data/system/dropbox
    this(context, new File("/data/system/dropbox"), FgThread.get().getLooper());
}

public DropBoxManagerService(final Context context, File path, Looper looper) {
    super(context);
    mDropBoxDir = path;
    ...
}

// 最終其實(shí)就是 IO 寫入
public void add(DropBoxManager.Entry entry) {
    File temp = null;
    InputStream input = null;
    OutputStream output = null;
    final String tag = entry.getTag();
    try {
        int flags = entry.getFlags();
        if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException();

        init();
        if (!isTagEnabled(tag)) return;
        long max = trimToFit();
        long lastTrim = System.currentTimeMillis();

        byte[] buffer = new byte[mBlockSize];
        input = entry.getInputStream();

        // First, accumulate up to one block worth of data in memory before
        // deciding whether to compress the data or not.

        int read = 0;
        while (read < buffer.length) {
            int n = input.read(buffer, read, buffer.length - read);
            if (n <= 0) break;
            read += n;
        }

        // If we have at least one block, compress it -- otherwise, just write
        // the data in uncompressed form.
		
		// mDropBoxDir 就是 /data/system/dropbox 目錄
        temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
        int bufferSize = mBlockSize;
        if (bufferSize > 4096) bufferSize = 4096;
        if (bufferSize < 512) bufferSize = 512;
        FileOutputStream foutput = new FileOutputStream(temp);
        output = new BufferedOutputStream(foutput, bufferSize);
        if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) {
            output = new GZIPOutputStream(output);
            flags = flags | DropBoxManager.IS_GZIPPED;
        }

        do {
            output.write(buffer, 0, read);

            long now = System.currentTimeMillis();
            if (now - lastTrim > 30 * 1000) {
                max = trimToFit();  // In case data dribbles in slowly
                lastTrim = now;
            }

            read = input.read(buffer);
            if (read <= 0) {
                FileUtils.sync(foutput);
                output.close();  // Get a final size measurement
                output = null;
            } else {
                output.flush();  // So the size measurement is pseudo-reasonable
            }

            long len = temp.length();
            if (len > max) {
                Slog.w(TAG, "Dropping: " + tag + " (" + temp.length() + " > " + max + " bytes)");
                temp.delete();
                temp = null;  // Pass temp = null to createEntry() to leave a tombstone
                break;
            }
        } while (read > 0);

        long time = createEntry(temp, tag, flags);
        temp = null;

        final Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
        dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
        dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
        if (!mBooted) {
            dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }
        // Call sendBroadcast after returning from this call to avoid deadlock. In particular
        // the caller may be holding the WindowManagerService lock but sendBroadcast requires a
        // lock in ActivityManagerService. ActivityManagerService has been caught holding that
        // very lock while waiting for the WindowManagerService lock.
        mHandler.sendMessage(mHandler.obtainMessage(MSG_SEND_BROADCAST, dropboxIntent));
    } catch (IOException e) {
        Slog.e(TAG, "Can't write: " + tag, e);
    } finally {
        IoUtils.closeQuietly(output);
        IoUtils.closeQuietly(input);
        entry.close();
        if (temp != null) temp.delete();
    }
}

當(dāng)發(fā)生異常信息時(shí),最終的異常信息是交給 DropBoxManager(具體說是 DropBoxManagerService)通過 IO 將信息寫入到指定目錄文件。

總結(jié)

上面主要分析了當(dāng)發(fā)生 java crash、native crash 和 anr 時(shí),Android 為我們做了哪些事情,在這里在簡單總結(jié)一下:

  • java crash 由 JVM 觸發(fā)處理,最終走到 /data/system/dropbox 目錄用文件保存

  • native crash 由管道通信建立 socket 接收通知,最終走到 /data/system/dropbox 目錄用文件保存

  • anr 由多種情況(事件、前后臺服務(wù))觸發(fā)器處理,最終走到 /data/system/dropbox 目錄用文件保存

所有的 crash 處理在 Android 系統(tǒng)內(nèi)部都會將對應(yīng)的數(shù)據(jù)收集到 /data/system/dropbox 目錄下。

同時(shí)我們也梳理了 Android 的 crash 處理機(jī)制:

  • Java 沒有捕獲異常時(shí)會由 JVM 調(diào)用 dispatchUncaughtException 調(diào)用一個 UncaughtExceptionHandler 處理,默認(rèn) RuntimeInit 提供了一個 KillApplicationHandler 會直接退出進(jìn)程。

如果我們要自己處理異常,可以自定義一個 UncaughtExceptionHandler 攔截處理。文章來源地址http://www.zghlxwxcb.cn/news/detail-505204.html

到了這里,關(guān)于Android 性能優(yōu)化系列:崩潰原因及捕獲的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • MySQL SQL性能分析,快速排查SQL執(zhí)行慢的原因(SQL優(yōu)化 一)

    MySQL SQL性能分析,快速排查SQL執(zhí)行慢的原因(SQL優(yōu)化 一)

    在開發(fā)和維護(hù)數(shù)據(jù)庫應(yīng)用程序時(shí),優(yōu)化SQL查詢的性能是至關(guān)重要的。MySQL提供了一些強(qiáng)大的工具和技術(shù),幫助我們進(jìn)行SQL性能分析,找出潛在的瓶頸并進(jìn)行相應(yīng)的優(yōu)化。 查看SQL的執(zhí)行頻率 show [ session| global ] status 命令查看服務(wù)器狀態(tài)信息,可以查看當(dāng)前數(shù)據(jù)庫的INSERT、UPDATE、

    2024年02月07日
    瀏覽(447)
  • Android 性能優(yōu)化——APP啟動優(yōu)化

    ??????? 首先在《Android系統(tǒng)和APP啟動流程》中我們介紹了 APP 的啟動流程,但都是 FW 層的流程,這里我們主要分析一下在 APP 中的啟動流程。要了解 APP 層的啟動流程,首先要了解 APP 啟動的分類。 冷啟動 ????????應(yīng)用從頭開始啟動,即應(yīng)用的首次啟動。需要做大量的工

    2024年04月12日
    瀏覽(35)
  • Android復(fù)雜UI的性能優(yōu)化實(shí)踐 - PTQBookPageView 性能優(yōu)化記錄

    Android復(fù)雜UI的性能優(yōu)化實(shí)踐 - PTQBookPageView 性能優(yōu)化記錄

    作者:彭泰強(qiáng) 要做性能優(yōu)化,首先得知道性能怎么度量、怎么表示。因?yàn)樾阅苁且粋€很抽象的詞,我們必須把它量化、可視化。那么,因?yàn)槭荱I組件優(yōu)化,我首先選用了 GPU呈現(xiàn)模式分析 這一工具。 在手機(jī)上的開發(fā)者模式里可以開啟 GPU呈現(xiàn)(渲染)模式分析 這一工具,有的

    2024年02月14日
    瀏覽(28)
  • Android 性能優(yōu)化(六):啟動優(yōu)化的詳細(xì)流程

    Android 性能優(yōu)化(六):啟動優(yōu)化的詳細(xì)流程

    書接上文,Android 性能優(yōu)化(一):閃退、卡頓、耗電、APK 從用戶體驗(yàn)角度有四個性能優(yōu)化方向: 追求穩(wěn)定,防止崩潰 追求流暢,防止卡頓 追求續(xù)航,防止耗損 追求精簡,防止臃腫 卡頓的場景通常與用戶交互體驗(yàn)最直接,分別為UI、啟動、跳轉(zhuǎn)、響應(yīng)四個方面,如下圖所示

    2024年04月17日
    瀏覽(39)
  • Android性能優(yōu)化—ViewPagers + Fragment緩存優(yōu)化

    Android性能優(yōu)化—ViewPagers + Fragment緩存優(yōu)化

    大家看標(biāo)題,可能會有點(diǎn)兒懵,什么是ViewPagers,因?yàn)樵诤芫弥?,我們使用的都是ViewPager,但是現(xiàn)在更多的是在用ViewPager2,因此用ViewPagers(ViewPager、ViewPager2)來代替兩者,主要介紹兩者的區(qū)別。 ViewPagers嵌套Fragment架構(gòu),在我們常用的App中隨處可見,抖音的首頁、各大電商

    2024年02月01日
    瀏覽(20)
  • QT--崩潰原因分析

    QT--崩潰原因分析

    本文為學(xué)習(xí)記錄,若有錯誤,請聯(lián)系作者,謙虛受教。 你從來來去自由,若你不想要了跑開便是。 發(fā)布的客戶版本里分析崩潰原因,便于解決問題。 在自己QT安裝的目錄下,例如:D:QtQt5.12.3Toolsmingw730_32bin,找到adde2line.exe。 將add2line.exe復(fù)制到自己發(fā)布的版本中。 在代碼

    2024年02月13日
    瀏覽(27)
  • Android中級——性能優(yōu)化

    Android中級——性能優(yōu)化

    畫面流暢需要幀數(shù)為60幀每秒 Android通過VSYNC信號觸發(fā)對UI的繪制,其間隔時(shí)間是1000ms/60=16ms(即1000ms內(nèi)顯示60幀畫面的單位時(shí)間) 故需在16ms之內(nèi)完成繪制才可以保證畫面的流暢 否則會造成丟幀,如一次繪制耗時(shí)20ms,當(dāng)16ms時(shí)系統(tǒng)發(fā)出VSYNC信號還未繪制完,下一個幀就會被丟棄,

    2023年04月20日
    瀏覽(23)
  • Android系統(tǒng)-性能-優(yōu)化概述

    目錄 引言: APP優(yōu)化: 網(wǎng)絡(luò)優(yōu)化: 內(nèi)存優(yōu)化: 卡頓優(yōu)化: 先大概對Android性能優(yōu)化做一個簡單分類和梳理。由于性能影響因素多,比如本文分類的APP,內(nèi)存,網(wǎng)絡(luò),卡頓都是互相影響的??D應(yīng)該是用戶最直觀可見的性能問題了。 APP優(yōu)化側(cè)重于啟動,UI繪制以及資源優(yōu)化這三

    2024年02月10日
    瀏覽(18)
  • ubuntu系統(tǒng)崩潰通過日志查看原因

    里面的日志包括: /var/log/syslog Syslog是一種標(biāo)準(zhǔn)的日志記錄工具。它收集包括內(nèi)核在內(nèi)的各種程序和服務(wù)的消息,并根據(jù)設(shè)置將它們存儲在通常位于 /var/log .?在某些數(shù)據(jù)中心設(shè)置中,有 數(shù)百個 設(shè)備,每個設(shè)備都有自己的日志; syslog 在這里也很方便。一個人只需設(shè)置一個專用

    2024年02月05日
    瀏覽(23)
  • Angular系列教程之變更檢測與性能優(yōu)化

    Angular系列教程之變更檢測與性能優(yōu)化

    Angular 除了默認(rèn)的變化檢測機(jī)制,也提供了ChangeDetectionStrategy.OnPush,用 OnPush 可以跳過某個組件或者某個父組件以及它下面所有子組件的變化檢測。 在本文中,我們將探討Angular中的變更檢測機(jī)制,并通過示例代碼來說明其工作原理。 當(dāng)我們在 model 中改變數(shù)據(jù)時(shí),框架層需要

    2024年01月17日
    瀏覽(16)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包