Android Glide圖片加載框架(三)緩存機制

文章目錄

  • 一、緩存簡介
  • 二、緩存用法
    • 內存緩存方式
    • 磁盤緩存方式
  • 三、緩存KEY
  • 四、內存緩存
    • 內存緩存流程
  • 五、磁盤緩存
    • 磁盤緩存流程


Android Glide圖片加載框架系列文章

Android Glide圖片加載框架(一)基本用法

Android Glide圖片加載框架(二)源碼解析之with()

Android Glide圖片加載框架(二)源碼解析之load()

Android Glide圖片加載框架(二)源碼解析之into()

Android Glide圖片加載框架(三)緩存機制


一、緩存簡介


Glide的緩存設計可以說是非常先進的,考慮的場景也很周全。在緩存這一功能上,Glide又將它分成了兩個模塊,一個是 內存緩存 ,一個是 磁盤緩存

這兩個緩存模塊的作用各不相同,

  • 內存緩存 的主要作用是防止應用重復將圖片數據讀取到內存當中;

  • 磁盤緩存 的主要作用是防止應用重復從網絡或其他地方重復下載和讀取數據。

內存緩存硬盤緩存 的相互結合才構成了Glide極佳的圖片緩存效果,那么接下來我們就分別來分析一下這兩種緩存的使用方法以及它們的實現原理。


二、緩存用法


內存緩存方式

RequestOptions options = new RequestOptions();
// 禁止內存緩存
options.skipMemoryCache(true);Glide.with(this).load(url).apply(options).into(imageView);

磁盤緩存方式

RequestOptions options = new RequestOptions();
// 磁盤不緩存
options.diskCacheStrategy(DiskCacheStrategy.NONE);Glide.with(this).load(url).apply(options).into(imageView);

可以設置5種模式:

  • DiskCacheStrategy.NONE: 表示不緩存任何內容

  • DiskCacheStrategy.DATA: 表示只緩存原始圖片

  • DiskCacheStrategy.RESOURCE: 表示只緩存轉換過后的圖片

  • DiskCacheStrategy.ALL: 表示既緩存原始圖片,也緩存轉換過后的圖片

  • DiskCacheStrategy.AUTOMATIC: 表示讓Glide根據圖片資源智能地選擇使用哪一種緩存策略(默認選項)


三、緩存KEY


既然是緩存功能,就必然會有用于進行緩存的Key。那么Glide的緩存Key是怎么生成的呢?我不得不說,Glide的緩存Key生成規則非常繁瑣,決定緩存Key的參數竟然有8個之多。不過繁瑣歸繁瑣,至少邏輯還是比較簡單的,我們先來看一下Glide緩存Key的生成邏輯。

生成緩存Key的代碼在 Engine 類的 load() 方法當中,這部分代碼我們在上一篇文章當中已經分析過了,只不過當時忽略了緩存相關的內容,那么我們現在重新來看一下:

public class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,EngineResource.ResourceListener {...public <R> LoadStatus load(GlideContext glideContext,Object model,Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform,Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb) {Util.assertMainThread();long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,resourceClass, transcodeClass, options);...}...
}

第27行可見,決定緩存Key的條件非常多,即使你用override()方法改變了一下圖片的width或者height,也會生成一個完全不同的緩存Key。

緩存key是一個 EngineKey 對象,該類重寫了 equals()hashCode() 方法,保證只有傳入EngineKey的所有參數都相同的情況下才認為是同一個 EngineKey 對象,代碼如下:

class EngineKey implements Key {...public boolean equals(Object o) {if (o instanceof EngineKey) {EngineKey other = (EngineKey) o;return model.equals(other.model)&& signature.equals(other.signature)&& height == other.height&& width == other.width&& transformations.equals(other.transformations)&& resourceClass.equals(other.resourceClass)&& transcodeClass.equals(other.transcodeClass)&& options.equals(other.options);}return false;}@Overridepublic int hashCode() {if (hashCode == 0) {hashCode = model.hashCode();hashCode = 31 * hashCode + signature.hashCode();hashCode = 31 * hashCode + width;hashCode = 31 * hashCode + height;hashCode = 31 * hashCode + transformations.hashCode();hashCode = 31 * hashCode + resourceClass.hashCode();hashCode = 31 * hashCode + transcodeClass.hashCode();hashCode = 31 * hashCode + options.hashCode();}return hashCode;}...
}

四、內存緩存


默認情況下,Glide自動就是開啟內存緩存的 。也就是說,當我們使用Glide加載了一張圖片之后,這張圖片就會被緩存到內存當中,只要在它還沒從內存中被清除之前,下次使用Glide再加載這張圖片都會直接從內存當中讀取,而不用重新從網絡或硬盤上讀取了,這樣無疑就可以大幅度提升圖片的加載效率。比方說你在一個RecyclerView當中反復上下滑動,RecyclerView中只要是Glide加載過的圖片都可以直接從內存當中迅速讀取并展示出來,從而大大提升了用戶體驗。

而Glide最為人性化的是,你甚至不需要編寫任何額外的代碼就能自動享受到這個極為便利的內存緩存功能,因為Glide默認就已經將它開啟了。

那么既然已經默認開啟了這個功能,還有什么可講的用法呢?只有一點,如果你有什么特殊的原因需要禁用內存緩存功能,Glide對此提供了接口:

RequestOptions options = new RequestOptions();
options.skipMemoryCache(true);Glide.with(this).load(url).apply(options).into(img);

可以看到,只需要調用skipMemoryCache()方法并傳入true,就表示禁用掉Glide的內存緩存功能

接下來就讓我們就通過閱讀源碼來分析一下Glide的內存緩存功能是如何實現的。

內存緩存使用弱引用和LruCache算法結合完成的,弱引用來緩存的是正在使用中的圖片。圖片封裝類Resources內部有個計數器判斷是該圖片否正在使用。


內存緩存流程

  • 讀: 是先從弱引用中取,取不到再從lruCache取;

  • 存: 內存緩存取不到,從網絡拉取回來先放在弱引用里,渲染圖片,圖片對象Resources使用計數加一;

  • 渲染完圖片: 圖片對象Resources使用計數減一,如果計數為0,圖片緩存從弱引用中刪除,放入lruCache緩存。

上篇提到,Engine 在加載流程的中的入口方法是 load 方法:

public class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,EngineResource.ResourceListener {...public <R> LoadStatus load(GlideContext glideContext,Object model,Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform,Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb) {Util.assertMainThread();long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;// 生成緩存keyEngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,resourceClass, transcodeClass, options);// 從弱引用獲取圖片EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);if (active != null) {cb.onResourceReady(active, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from active resources", startTime, key);}return null;}// 從LruCache獲取緩存圖片EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);if (cached != null) {cb.onResourceReady(cached, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from cache", startTime, key);}return null;}EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);if (current != null) {current.addCallback(cb);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Added to existing load", startTime, key);}return new LoadStatus(cb, current);}EngineJob<R> engineJob =engineJobFactory.build(key,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache);DecodeJob<R> decodeJob =decodeJobFactory.build(glideContext,model,key,signature,width,height,resourceClass,transcodeClass,priority,diskCacheStrategy,transformations,isTransformationRequired,isScaleOnlyOrNoTransform,onlyRetrieveFromCache,options,engineJob);jobs.put(key, engineJob);engineJob.addCallback(cb);engineJob.start(decodeJob);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Started new load", startTime, key);}return new LoadStatus(cb, engineJob);}...
}

上面是從內存緩存中讀取圖片的主流程:

  • 生成緩存的key。

  • 從弱引用獲取圖片。

  • 弱引用沒取到,在從LruCache獲取緩存圖片。

  • 內存緩存取不到,進入異步處理。

我們具體看取圖片的兩個方法 loadFromActiveResources()loadFromCache()

  • loadFromActiveResources 使用的就是弱引用。

  • loadFromCache 使用的就是LruCache算法。

我們來看一下它們的源碼:

public class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,EngineResource.ResourceListener {...private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {if (!isMemoryCacheable) {return null;}EngineResource<?> active = activeResources.get(key);if (active != null) {active.acquire();}return active;}private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {if (!isMemoryCacheable) {return null;}EngineResource<?> cached = getEngineResourceFromCache(key);if (cached != null) {cached.acquire();activeResources.activate(key, cached);}return cached;}private EngineResource<?> getEngineResourceFromCache(Key key) {Resource<?> cached = cache.remove(key);final EngineResource<?> result;if (cached == null) {result = null;} else if (cached instanceof EngineResource) {// Save an object allocation if we've cached an EngineResource (the typical case).result = (EngineResource<?>) cached;} else {result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);}return result;}...
}

loadFromActiveResources() 方法:

  • 首先就判斷 isMemoryCacheable 是不是 false ,如果是false的話就直接返回null。這就是 skipMemoryCache() 方法設置的是否內存緩存已被禁用。

  • 然后從 activeResources 當中取值,使用activeResources來緩存正在使用中的圖片,用來保護正在使用中的圖片不會被LruCache算法回收掉。


loadFromCache() 方法:

  • 首先就判斷 isMemoryCacheable 是不是 false ,如果是false的話就直接返回null。這就是 skipMemoryCache() 方法設置的是否內存緩存已被禁用。

  • 然后調用 getEngineResourceFromCache() 方法來獲取緩存。在這個方法中,會從中獲取圖片緩存 LruResourceCache ,LruResourceCache其實使用的就是LruCache算法實現的緩存。

  • 當我們從 LruResourceCache 中獲取到緩存圖片之后會將它從緩存中移除,將緩存圖片存儲到 activeResources 當中。activeResources就是弱引用的HashMap,用來緩存正在使用中的圖片。


這樣我們把從內存讀取圖片緩存的流程搞清了,那是什么時候存儲的呢。想想什么時候合適?是不是應該在異步處理獲取到圖片后,再緩存到內存?

EngineJob 獲取到圖片后 會回調Engine的 onEngineJobComplete() 。我們來看下做了什么:

public class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,EngineResource.ResourceListener {...public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {Util.assertMainThread();// A null resource indicates that the load failed, usually due to an exception.if (resource != null) {resource.setResourceListener(key, this);if (resource.isCacheable()) {activeResources.activate(key, resource);}}jobs.removeIfCurrent(key, engineJob);}...
}

onEngineJobComplete() 方法里將正在加載的圖片放到弱引用緩存。那什么時候放在LruCache里呢?當然是在使用完,那什么時候使用完呢?

那我們來看 EngineResource 這個類是怎么標記自己是否在被使用的。EngineResource是用一個acquired變量用來記錄圖片被引用的次數,調用acquire()方法會讓變量加1,調用release()方法會讓變量減1,代碼如下所示:

class EngineResource<Z> implements Resource<Z> {...private int acquired;void acquire() {if (isRecycled) {throw new IllegalStateException("Cannot acquire a recycled resource");}if (!Looper.getMainLooper().equals(Looper.myLooper())) {throw new IllegalThreadStateException("Must call acquire on the main thread");}++acquired;}void release() {if (acquired <= 0) {throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");}if (!Looper.getMainLooper().equals(Looper.myLooper())) {throw new IllegalThreadStateException("Must call release on the main thread");}if (--acquired == 0) {listener.onResourceReleased(key, this);}}...
}

可以看出當引用計數acquired變量為0,就是沒有在使用了,然后調用了 listener.onResourceReleased(key, this);

這個 listener 就是 Engine 對象,我們來看下它的 onResourceReleased() 方法:

public class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,EngineResource.ResourceListener {...public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {Util.assertMainThread();activeResources.deactivate(cacheKey);if (resource.isCacheable()) {cache.put(cacheKey, resource);} else {resourceRecycler.recycle(resource);}}...
}

做了三件事:

  • 從弱引用刪除圖片緩存

  • 是否支持緩存,緩存到LruCache緩存

  • 不支持緩存直接調用垃圾回收,回收圖片

到這里內存緩存的讀和存的流程就介紹完了,根據源碼回頭看看我們之前列的Glide內存緩存流程,就清晰很多了。


五、磁盤緩存


磁盤緩存流程

  • 讀: 先找處理后(result)的圖片,沒有的話再找原圖。

  • 存: 先存原圖,再存處理后的圖。

注: diskCacheStrategy設置的的緩存模式即影響讀取,也影響存儲。

在判斷了兩級內存緩存之后,如果拿不到緩存,就會接著創建 EngineJobDecodeJob ,然后接著就會調用進 DecodeJob 線程的 run() 方法:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...@Overridepublic void run() {// This should be much more fine grained, but since Java's thread pool implementation silently// swallows all otherwise fatal exceptions, this will at least make it obvious to developers// that something is failing.GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);// Methods in the try statement can invalidate currentFetcher, so set a local variable here to// ensure that the fetcher is cleaned up either way.DataFetcher<?> localFetcher = currentFetcher;try {if (isCancelled) {notifyFailed();return;}runWrapped();} catch (Throwable t) {// Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our// usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We// are however ensuring that our callbacks are always notified when a load fails. Without this// notification, uncaught throwables never notify the corresponding callbacks, which can cause// loads to silently hang forever, a case that's especially bad for users using Futures on// background threads.if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "DecodeJob threw unexpectedly"+ ", isCancelled: " + isCancelled+ ", stage: " + stage, t);}// When we're encoding we've already notified our callback and it isn't safe to do so again.if (stage != Stage.ENCODE) {throwables.add(t);notifyFailed();}if (!isCancelled) {throw t;}} finally {// Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call// close in all cases anyway.if (localFetcher != null) {localFetcher.cleanup();}GlideTrace.endSection();}}private void runWrapped() {switch (runReason) {case INITIALIZE:stage = getNextStage(Stage.INITIALIZE);currentGenerator = getNextGenerator();runGenerators();break;case SWITCH_TO_SOURCE_SERVICE:runGenerators();break;case DECODE_DATA:decodeFromRetrievedData();break;default:throw new IllegalStateException("Unrecognized run reason: " + runReason);}}...
}

run() 中主要還是調用的 runWrapper() 方法,繼而調用 runGenerator()

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...private void runGenerators() {currentThread = Thread.currentThread();startFetchTime = LogTime.getLogTime();boolean isStarted = false;while (!isCancelled && currentGenerator != null&& !(isStarted = currentGenerator.startNext())) {stage = getNextStage(stage);currentGenerator = getNextGenerator();if (stage == Stage.SOURCE) {reschedule();return;}}// We've run out of stages and generators, give up.if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {notifyFailed();}// Otherwise a generator started a new load and we expect to be called back in// onDataFetcherReady.}...
}

這里調用了一個循環獲取解析生成器 Generator 的方法,而解析生成器有多個實現類:ResourcesCacheGeneratorSourceGeneratorDataCacheGenerator,它們負責各種硬盤緩存策略下的緩存管理,所以這里關鍵的條件在于 currentGenerator.startNext() 循環獲取每個Generator能否獲取到緩存,獲取不到就通過 getNextGenerator() 進行下一種:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...private DataFetcherGenerator getNextGenerator() {switch (stage) {case RESOURCE_CACHE:return new ResourceCacheGenerator(decodeHelper, this);case DATA_CACHE:return new DataCacheGenerator(decodeHelper, this);case SOURCE:return new SourceGenerator(decodeHelper, this);case FINISHED:return null;default:throw new IllegalStateException("Unrecognized stage: " + stage);}}...
}

所以我們看看 ResourceCacheGenerator.startNext() ,看下它是用什么來緩存的,其中部分代碼如下:

class ResourceCacheGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object> {...public boolean startNext() {...while (modelLoaders == null || !hasNextModelLoader()) {...Key sourceId = sourceIds.get(sourceIdIndex);Class<?> resourceClass = resourceClasses.get(resourceClassIndex);Transformation<?> transformation = helper.getTransformation(resourceClass);currentKey =new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoopshelper.getArrayPool(),sourceId,helper.getSignature(),helper.getWidth(),helper.getHeight(),transformation,resourceClass,helper.getOptions());cacheFile = helper.getDiskCache().get(currentKey);if (cacheFile != null) {sourceKey = sourceId;modelLoaders = helper.getModelLoaders(cacheFile);modelLoaderIndex = 0;}}...return started;}...
}

這里通過一個資源的關鍵信息生成key,然后調用 helper.getDiskCache().get() ,我們跟進去 DiskCache 看看:

final class DecodeHelper<Transcode> {...DiskCache getDiskCache() {return diskCacheProvider.getDiskCache();}...
}
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...interface DiskCacheProvider {DiskCache getDiskCache();}...
}

可以看到最終是調用了 DiskCacheProvider 接口的 getDiskCache() 方法獲取一個 DiskCache 對象,那么這個D對象又是什么來頭呢?

public interface DiskCache {...
}

可以看到這是一個用來緩存硬盤數據的接口,那么它的實現就是我們要找的最終目標:

public class DiskLruCacheWrapper implements DiskCache {...private DiskLruCache diskLruCache;...
}

里面的就不詳細分析下去了,這里主要維護了一個 DiskLruCache ,Glide就是通過這個來實現硬盤緩存的。

可以看到Glide的硬盤緩存是依靠DiskLruCache來進行緩存的,同樣也是Lru算法。

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

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

相關文章

計算機操作系統 死鎖問題

概念 條件是基礎&#xff0c;在一定的原因下&#xff0c;產生結果死鎖三胞胎 死鎖 僵持&#xff0c;消耗時間&#xff0c;雙方都占用了部分資源&#xff0c;不釋放活鎖 雙方互相謙讓&#xff0c;都不占用資源饑餓 謙讓的一方一直等待&#xff0c;無法占有資源&#xff0c;導致…

C++ 力扣劍指Offer16-數值的整數次方

題目詳情 /* * 實現函數double Power(double base, int exponent)&#xff0c; * 求base的exponent次方。不得使用庫函數&#xff0c;同時不需要考慮大數問題。示例 1: 輸入: 2.00000, 10 輸出: 1024.00000示例 2: 輸入: 2.10000, 3 輸出: 9.26100 * 示例 3: 輸入: 2.00000, -…

Android Glide圖片加載框架(四)回調與監聽

文章目錄Android Glide圖片加載框架系列文章 Android Glide圖片加載框架&#xff08;一&#xff09;基本用法 Android Glide圖片加載框架&#xff08;二&#xff09;源碼解析之with() Android Glide圖片加載框架&#xff08;二&#xff09;源碼解析之load() Android Glide圖…

算法章節 數組、鏈表、棧、隊列

數組 概念與特性 1&#xff0c;數組是線性表&#xff0c;用一組連續的內存空間存儲?組具有相同類型的數據 2&#xff0c;最大的特性是?持按照下標O(1)時間復雜度內快速訪問數組元素 3&#xff0c;?維數組尋址公式&#xff1a;a[i]_addr base_addr i * data_type_size 操作…

武忠祥.高等數學.基礎課-第一章函數 極限 連續P10

sin(1/x) 詳細解析網址 1.圖像 2.極限 x–>0時,函數極限不存在 sin2x 詳細作圖網址 1.圖像 2.周期為Π f(x)周期為T,f(axb)周期為T/|a| 所以sinx周期為2Π,sin2x周期為2Π/2Π |sinx| 詳細講解網址 1.圖像 2.周期:Π 3.絕對值 &#xff08;1&#xff09;y|sinx|的圖…

Java命令:jstat — 查看JVM的GC信息

文章目錄一、簡介二、常用命令1、jstat -class pid : class loader行為統計2、jstat -compiler pid : JIT編譯器行為統計3、jstat -gc pid 5000 20 : 垃圾回收堆行為統計4、jstat -gccapacity pid 5000 20 : 堆內存統計5、jstat -gcutil pid 5000 20 : 總結垃圾回收統計6、jsta…

算法章節 遞歸、排序、?分查找

遞歸 概念與特性函數調?函數?身的編程?式叫做遞歸&#xff0c;調?為”遞“&#xff0c;返回為”歸“三個條件1. ?個問題的解可以分解為多個?問題的解&#xff1b; 2. 分解之后的?問題&#xff0c;除了數據規模不同&#xff0c;求解思路跟原問題相同&#xff1b; 3. 存在…

codeforces 50A-C語言解題報告

50A題目網址 解題報告-others 題目解析 1.輸入n x m大小的木板,使用21大小的多米諾去填滿,求最多的多米諾數目 2.通過分析把木板分為奇數和偶數的情況 1)有一邊是偶數的情況: 使用2去填滿 2)兩個邊都是奇數 奇數-1偶數 還是讓木板的(奇數-1)邊去和2平行,再加上 (m-1)/2(n/1)…

Java命令:jps — 查看進程信息

文章目錄一、簡介二、常用命令1、jps2、jps -l3、jps -q4、jps -m5、jps -v6、jps失效一、簡介 JVM Process Status Tool&#xff0c;顯示指定系統內所有的HotSpot虛擬機進程。 功能&#xff1a; 顯示當前所有java進程pid的命令&#xff0c;我們可以通過這個命令來查看到底啟…

操作系統概述 記錄操作系統相關知識

操作系統 現代計算機系統由一個或多個處理器、主存、打印機、鍵盤、鼠標、顯示器、網絡接口以及各種輸入/輸出設備構成。上面提到的這些東西都屬于硬件資源&#xff0c;用戶不會直接和硬件進行交互&#xff0c;計算機安裝了一層軟件&#xff0c;這層軟件能夠通過響應用戶輸入的…

2014年英語一作文partA

作文講解網址 題目 Write a letter of about 100 words to the president of your university, suggesting how to improve students’ physical condition. You should include the details you think necessary. You should write neatly on the ANSWER SHEET. Do not sign…

JDK工具使用大全

文章目錄一、簡介一、簡介 在JDK的bin目錄下有很多命令行工具&#xff1a; 常用工具使用詳解如下&#xff1a; Java命令&#xff1a;jps — 查看進程信息 Java命令&#xff1a;jstack — 獲取線程dump信息 Java命令&#xff1a;jmap — 打印指定進程的共享對象內存映射或…

Linux進程 excel族函數的用法

介紹 使用fork創建一個進程之后&#xff0c;經常會在新進程中調用exec函數執行別的程序當前進程調用exec函數之后&#xff0c;這個進程會被完全替代換成新的程序&#xff0c;即便如此仍然是同一個進程&#xff0c;進程ID不變函數族 execl execlp execle execvp execvpe頭文件 …

codeforces 118A-C語言解題報告

118A題目網址 題目解析 1.輸入一個英語字符串,要求把其中的元音字母刪去(元音是字母“A”、“O”、“Y”、“E”、“U”、“I”包括大小寫&#xff0c;其余的是輔音),把剩余的輔音字母全部變為小寫,并在每一個輔音字母之前加上一個. 如: 輸入: Codeforces 輸出: .c.d.f.r.c.s…

ArrayList和HashMap遍歷比較

目錄一、ArrayList遍歷方式1、普通for循環遍歷2、增強for循環遍歷3、Iterator迭代器遍歷4、三種方式比較二、Map遍歷方式1、增強for循環 keySet() 遍歷2、增強for循環 entrySet() 遍歷3、Iterator keySet() 遍歷4、Itorator entrySet() 遍歷5、四種方式比較三、java開發手冊…

C++primer 12章 動態內存和智能指針

C引入智能指針的目的 使用智能指針來管理動態分配的對象&#xff0c;當一個對象應該被釋放的時候&#xff0c;指向他的智能指針確保自動釋放它 內存分配 靜態內存&#xff1a;局部static對象、類static數據成員、定義在任何函數之外的變量棧內存&#xff1a;定義在函數內的非…

Mac下iTerm2的安裝與配置

目錄一、iTerm2簡介二、下載以及安裝三、iTerm2主題配置四、配置Oh My Zsh1、安裝方式&#xff08;1&#xff09;一鍵安裝&#xff08;2&#xff09;手動安裝3、切換zsh4、修改主題五、配置Meslo字體六、聲明高亮七、自動建議填充八、iTerm2快速隱藏和顯示九、iTerm2隱藏用戶名…

codeforces 282A-C語言解題報告

282A題目網址 題目解析 1.第一行輸入n(表示有n條語句都要執行),再輸入X,X(都表示X1),–X,X–(都表示X-1),最初X0,輸出X的值 2.使用字符數組去存放每一行的字符串,因為字符串,所以直接整體存入scanf("%s",c); 3.因為字符數組最后一個是’\0’去表示末尾,所以要開辟…

Java命令:jinfo — 查看進程參數

目錄一、簡介二、常用命令1、jinfo -flags pid : 打印當前指定java進程中已經設定的所有JVM參數信息2、jinfo -flag pid : 打印指定名稱的參數3、jinfo -flag [|-] pid : 打開或關閉參數4、jinfo -sysprops pid : 打印當前java進程中設定的系統環境參數一、簡介 jinfo 是 JDK …

C++primer第八章 IO庫 8.1 IO類

IO庫設施 istream &#xff08;輸入流&#xff09;類型&#xff0c;提供輸入操作。ostream &#xff08;輸出流&#xff09;類型&#xff0c;提供輸出操作。cin,—個 istream對象&#xff0c;從標準輸入讀取數據。cout, 一個ostream對象&#xff0c;向標準輸出寫入數據。cerr…