垃圾回收(三)【垃圾回收通知】
在有些情況下,公共語言執行時執行的完整垃圾回收(即第 2 代回收)可能會對效能產生負面影響。 特別是,處理大量請求的伺服器可能會出現此問題;在這種情況下,長時間垃圾回收會導致請求超時。為了防止在關鍵時期發生完全回收,可以接收即將執行完全垃圾回收的通知,再採取措施將工作負載重定向到另一個伺服器例項。 也可以自行誘導回收,前提是當前伺服器例項不需要處理請求。
RegisterForFullGCNotification 方法註冊為,在執行時檢測到即將執行完全垃圾回收時發出通知。 此通知分為兩個部分:完全垃圾回收何時即將執行,以及完全垃圾回收何時完成。
警告
只有阻止垃圾回收會引發通知。 如果<gcConcurrent>
配置元素已啟用,後臺垃圾回收不會發出通知。
若要確定何時發出通知,請使用 WaitForFullGCApproach
和 WaitForFullGCComplete
方法。 通常,在 while
迴圈中使用這些方法,以持續獲取表示通知狀態的 GCNotificationStatus
列舉。 如果值為 Succeeded
,可以執行下列操作:
- 為了響應使用
WaitForFullGCApproach
方法獲得的通知,可以重定向工作負載,並能自行誘導回收。 - 為了響應使用
WaitForFullGCComplete
方法獲得的通知,可以讓當前伺服器例項再次用於處理請求。 也可以收集資訊。
例如,可以使用CollectionCount
方法記錄回收次數。
WaitForFullGCApproach
和 WaitForFullGCComplete
方法要配合使用。 使用一個方法,而不使用另一個方法,可能會生成不確定的結果。
完全垃圾回收
如果發生下列任一情況,執行時就會執行完全垃圾回收:
- 足夠多的記憶體已提升到第 2 代,導致執行下一個第 2 代回收。
- 足夠多的記憶體已提升到大型物件堆,導致執行下一個第 2 代回收。
- 由於其他因素,導致第 1 代回收升級為第 2 代回收。
在 RegisterForFullGCNotification
方法中指定的閾值適用於前兩種情況。 不過,在第一種情況下,不一定會在與指定的閾值相稱的時間收到通知,原因有下面兩個:
- 執行時不檢查每個小型物件分配(出於效能考慮)。
- 只有第 1 代回收將記憶體提升到第 2 代。
第三種情況也加劇了通知接收時間的不確定性。 可以在此期間重定向請求,或在可以更好適應時自行誘導回收,從而減輕不合時宜的完全垃圾回收造成的影響。儘管並不保證有效,但確實證明這是非常實用的方法。
通知閾值引數
RegisterForFullGCNotification
方法包含兩個引數,用於指定第 2 代物件和大型物件堆的閾值。 如果達到這些值,就應發出垃圾回收通知。 下表介紹了這些引數。
引數 | 描述 |
---|---|
maxGenerationThreshold | 介於 1 和 99 之間的數字,指定根據在第 2 代中提升的物件,應何時發出通知。 |
largeObjectHeapThreshold | 介於 1 和 99 之間的數字,指定根據大型物件堆中分配的物件,應何時發出通知。 |
如果指定的值過高,很可能會出現的情況是,將會收到通知,但在執行時執行回收前等待的時間太長。 如果自行誘導回收,回收的物件可能會多於在執行時執行回收時回收的物件。
如果指定的值過低,在執行時執行回收前等待通知的時間可能不夠長。
示例
描述
在下面的示例中,一組伺服器處理傳入的 Web 請求。 為了模擬處理請求的工作負載,將位元組陣列新增到 List<T>
集合中。 每個伺服器都會註冊獲取垃圾回收通知,再對 WaitForFullGCProc
使用者方法啟動執行緒,以持續監視 WaitForFullGCApproach
和 WaitForFullGCComplete
方法返回的 GCNotificationStatus
列舉。
在通知發出時,WaitForFullGCApproach
和 WaitForFullGCComplete
方法呼叫它們各自的事件處理使用者方法:
OnFullGCApproachNotify
此方法呼叫RedirectRequests
使用者方法,指示請求佇列伺服器暫停向伺服器傳送請求。 具體模擬方式是,將類級別變數bAllocate
設定為false
,這樣就不會再分配物件。
接下來,呼叫FinishExistingRequests
使用者方法,完成處理掛起的伺服器請求。 具體模擬方式是,清除List<T>
集合。
最後,由於工作負載很輕,誘導垃圾回收。OnFullGCCompleteNotify
此方法呼叫使用者方法AcceptRequests
以繼續接受請求,因為伺服器不再易受完全垃圾回收影響。 此操作的具體模擬方式是,將bAllocate
變數設定為true
,以便能夠繼續將物件新增到List<T>
集合。
下面的程式碼包含示例的 Main
方法。
using System;
using System.Collections.Generic;
using System.Threading;
namespace GCNotify
{
class Program
{
// Variable for continual checking in the
// While loop in the WaitForFullGCProc method.
static bool checkForNotify = false;
// Variable for suspending work
// (such servicing allocated server requests)
// after a notification is received and then
// resuming allocation after inducing a garbage collection.
static bool bAllocate = false;
// Variable for ending the example.
static bool finalExit = false;
// Collection for objects that
// simulate the server request workload.
static List<byte[]> load = new List<byte[]>();
public static void Main(string[] args)
{
try
{
// Register for a notification.
GC.RegisterForFullGCNotification(10, 10);
Console.WriteLine("Registered for GC notification.");
checkForNotify = true;
bAllocate = true;
// Start a thread using WaitForFullGCProc.
Thread thWaitForFullGC = new Thread(new ThreadStart(WaitForFullGCProc));
thWaitForFullGC.Start();
// While the thread is checking for notifications in
// WaitForFullGCProc, create objects to simulate a server workload.
try
{
int lastCollCount = 0;
int newCollCount = 0;
while (true)
{
if (bAllocate)
{
load.Add(new byte[1000]);
newCollCount = GC.CollectionCount(2);
if (newCollCount != lastCollCount)
{
// Show collection count when it increases:
Console.WriteLine("Gen 2 collection count: {0}", GC.CollectionCount(2).ToString());
lastCollCount = newCollCount;
}
// For ending the example (arbitrary).
if (newCollCount == 500)
{
finalExit = true;
checkForNotify = false;
break;
}
}
}
}
catch (OutOfMemoryException)
{
Console.WriteLine("Out of memory.");
}
finalExit = true;
checkForNotify = false;
GC.CancelFullGCNotification();
}
catch (InvalidOperationException invalidOp)
{
Console.WriteLine("GC Notifications are not supported while concurrent GC is enabled.\n"
+ invalidOp.Message);
}
}
public static void OnFullGCApproachNotify()
{
Console.WriteLine("Redirecting requests.");
// Method that tells the request queuing
// server to not direct requests to this server.
RedirectRequests();
// Method that provides time to
// finish processing pending requests.
FinishExistingRequests();
// This is a good time to induce a GC collection
// because the runtime will induce a full GC soon.
// To be very careful, you can check precede with a
// check of the GC.GCCollectionCount to make sure
// a full GC did not already occur since last notified.
GC.Collect();
Console.WriteLine("Induced a collection.");
}
public static void OnFullGCCompleteEndNotify()
{
// Method that informs the request queuing server
// that this server is ready to accept requests again.
AcceptRequests();
Console.WriteLine("Accepting requests again.");
}
public static void WaitForFullGCProc()
{
while (true)
{
// CheckForNotify is set to true and false in Main.
while (checkForNotify)
{
// Check for a notification of an approaching collection.
GCNotificationStatus s = GC.WaitForFullGCApproach();
if (s == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notification raised.");
OnFullGCApproachNotify();
}
else if (s == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// This can occur if a timeout period
// is specified for WaitForFullGCApproach(Timeout)
// or WaitForFullGCComplete(Timeout)
// and the time out period has elapsed.
Console.WriteLine("GC Notification not applicable.");
break;
}
// Check for a notification of a completed collection.
GCNotificationStatus status = GC.WaitForFullGCComplete();
if (status == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notification raised.");
OnFullGCCompleteEndNotify();
}
else if (status == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// Could be a time out.
Console.WriteLine("GC Notification not applicable.");
break;
}
}
Thread.Sleep(500);
// FinalExit is set to true right before
// the main thread cancelled notification.
if (finalExit)
{
break;
}
}
}
private static void RedirectRequests()
{
// Code that sends requests
// to other servers.
// Suspend work.
bAllocate = false;
}
private static void FinishExistingRequests()
{
// Code that waits a period of time
// for pending requests to finish.
// Clear the simulated workload.
load.Clear();
}
private static void AcceptRequests()
{
// Code that resumes processing
// requests on this server.
// Resume work.
bAllocate = true;
}
}
}
下面的程式碼包含 WaitForFullGCProc
使用者方法,其中包括持續 while
迴圈,用於檢查是否有垃圾回收通知。
public static void WaitForFullGCProc()
{
while (true)
{
// CheckForNotify is set to true and false in Main.
while (checkForNotify)
{
// Check for a notification of an approaching collection.
GCNotificationStatus s = GC.WaitForFullGCApproach();
if (s == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notification raised.");
OnFullGCApproachNotify();
}
else if (s == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// This can occur if a timeout period
// is specified for WaitForFullGCApproach(Timeout)
// or WaitForFullGCComplete(Timeout)
// and the time out period has elapsed.
Console.WriteLine("GC Notification not applicable.");
break;
}
// Check for a notification of a completed collection.
GCNotificationStatus status = GC.WaitForFullGCComplete();
if (status == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notification raised.");
OnFullGCCompleteEndNotify();
}
else if (status == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// Could be a time out.
Console.WriteLine("GC Notification not applicable.");
break;
}
}
Thread.Sleep(500);
// FinalExit is set to true right before
// the main thread cancelled notification.
if (finalExit)
{
break;
}
}
}
下面的程式碼包含 OnFullGCApproachNotify
方法,呼叫自WaitForFullGCProc
方法。
public static void OnFullGCApproachNotify()
{
Console.WriteLine("Redirecting requests.");
// Method that tells the request queuing
// server to not direct requests to this server.
RedirectRequests();
// Method that provides time to
// finish processing pending requests.
FinishExistingRequests();
// This is a good time to induce a GC collection
// because the runtime will induce a full GC soon.
// To be very careful, you can check precede with a
// check of the GC.GCCollectionCount to make sure
// a full GC did not already occur since last notified.
GC.Collect();
Console.WriteLine("Induced a collection.");
}
下面的程式碼包含 OnFullGCApproachComplete
方法,呼叫自WaitForFullGCProc
方法。
public static void OnFullGCCompleteEndNotify()
{
// Method that informs the request queuing server
// that this server is ready to accept requests again.
AcceptRequests();
Console.WriteLine("Accepting requests again.");
}
下面的程式碼包含呼叫自 OnFullGCApproachNotify
和 OnFullGCCompleteNotify
方法的使用者方法。 使用者方法重定向請求,完成現有請求,再在發生完全垃圾回收後繼續執行請求。
private static void RedirectRequests()
{
// Code that sends requests
// to other servers.
// Suspend work.
bAllocate = false;
}
private static void FinishExistingRequests()
{
// Code that waits a period of time
// for pending requests to finish.
// Clear the simulated workload.
load.Clear();
}
private static void AcceptRequests()
{
// Code that resumes processing
// requests on this server.
// Resume work.
bAllocate = true;
}
整個程式碼示例如下所示:
using System;
using System.Collections.Generic;
using System.Threading;
namespace GCNotify
{
class Program
{
// Variable for continual checking in the
// While loop in the WaitForFullGCProc method.
static bool checkForNotify = false;
// Variable for suspending work
// (such servicing allocated server requests)
// after a notification is received and then
// resuming allocation after inducing a garbage collection.
static bool bAllocate = false;
// Variable for ending the example.
static bool finalExit = false;
// Collection for objects that
// simulate the server request workload.
static List<byte[]> load = new List<byte[]>();
public static void Main(string[] args)
{
try
{
// Register for a notification.
GC.RegisterForFullGCNotification(10, 10);
Console.WriteLine("Registered for GC notification.");
checkForNotify = true;
bAllocate = true;
// Start a thread using WaitForFullGCProc.
Thread thWaitForFullGC = new Thread(new ThreadStart(WaitForFullGCProc));
thWaitForFullGC.Start();
// While the thread is checking for notifications in
// WaitForFullGCProc, create objects to simulate a server workload.
try
{
int lastCollCount = 0;
int newCollCount = 0;
while (true)
{
if (bAllocate)
{
load.Add(new byte[1000]);
newCollCount = GC.CollectionCount(2);
if (newCollCount != lastCollCount)
{
// Show collection count when it increases:
Console.WriteLine("Gen 2 collection count: {0}", GC.CollectionCount(2).ToString());
lastCollCount = newCollCount;
}
// For ending the example (arbitrary).
if (newCollCount == 500)
{
finalExit = true;
checkForNotify = false;
break;
}
}
}
}
catch (OutOfMemoryException)
{
Console.WriteLine("Out of memory.");
}
finalExit = true;
checkForNotify = false;
GC.CancelFullGCNotification();
}
catch (InvalidOperationException invalidOp)
{
Console.WriteLine("GC Notifications are not supported while concurrent GC is enabled.\n"
+ invalidOp.Message);
}
}
public static void OnFullGCApproachNotify()
{
Console.WriteLine("Redirecting requests.");
// Method that tells the request queuing
// server to not direct requests to this server.
RedirectRequests();
// Method that provides time to
// finish processing pending requests.
FinishExistingRequests();
// This is a good time to induce a GC collection
// because the runtime will induce a full GC soon.
// To be very careful, you can check precede with a
// check of the GC.GCCollectionCount to make sure
// a full GC did not already occur since last notified.
GC.Collect();
Console.WriteLine("Induced a collection.");
}
public static void OnFullGCCompleteEndNotify()
{
// Method that informs the request queuing server
// that this server is ready to accept requests again.
AcceptRequests();
Console.WriteLine("Accepting requests again.");
}
public static void WaitForFullGCProc()
{
while (true)
{
// CheckForNotify is set to true and false in Main.
while (checkForNotify)
{
// Check for a notification of an approaching collection.
GCNotificationStatus s = GC.WaitForFullGCApproach();
if (s == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notification raised.");
OnFullGCApproachNotify();
}
else if (s == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// This can occur if a timeout period
// is specified for WaitForFullGCApproach(Timeout)
// or WaitForFullGCComplete(Timeout)
// and the time out period has elapsed.
Console.WriteLine("GC Notification not applicable.");
break;
}
// Check for a notification of a completed collection.
GCNotificationStatus status = GC.WaitForFullGCComplete();
if (status == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notification raised.");
OnFullGCCompleteEndNotify();
}
else if (status == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// Could be a time out.
Console.WriteLine("GC Notification not applicable.");
break;
}
}
Thread.Sleep(500);
// FinalExit is set to true right before
// the main thread cancelled notification.
if (finalExit)
{
break;
}
}
}
private static void RedirectRequests()
{
// Code that sends requests
// to other servers.
// Suspend work.
bAllocate = false;
}
private static void FinishExistingRequests()
{
// Code that waits a period of time
// for pending requests to finish.
// Clear the simulated workload.
load.Clear();
}
private static void AcceptRequests()
{
// Code that resumes processing
// requests on this server.
// Resume work.
bAllocate = true;
}
}
}
相關文章
- 垃圾回收(一)【垃圾回收的基礎】
- 垃圾回收
- JVM 垃圾回收演算法和垃圾回收器JVM演算法
- [JVM]垃圾回收JVM
- golang垃圾回收Golang
- Python:垃圾回收Python
- JVM垃圾回收JVM
- 垃圾回收_上
- 垃圾回收_下
- javascript垃圾回收JavaScript
- GC垃圾回收器GC
- 【Postgresql】VACUUM 垃圾回收SQL
- JVM垃圾回收(下)JVM
- JVM垃圾回收概述JVM
- JVM垃圾回收器JVM
- JVM - 垃圾回收概述JVM
- Unity GC垃圾回收UnityGC
- JAVA垃圾回收機制和Python垃圾回收對比與分析JavaPython
- 【JVM】垃圾回收器總結(2)——七種垃圾回收器型別JVM型別
- jvm 自動垃圾回收JVM
- Java 垃圾回收機制Java
- JVM垃圾回收機制JVM
- Python垃圾回收機制Python
- Kubernetes 中的垃圾回收
- JVM-垃圾回收篇JVM
- JVM 中的垃圾回收JVM
- 聊聊Dotnet的垃圾回收
- 淺談JVM垃圾回收JVM
- java垃圾回收機制Java
- 垃圾回收(四)【弱引用】
- js垃圾回收機制JS
- JVM垃圾回收歷險JVM
- JVM 垃圾回收機制JVM
- javascript 垃圾回收機制JavaScript
- JavaScript 中的垃圾回收JavaScript
- jvm(4)---垃圾回收(哪些物件可以被回收)JVM物件
- PHP的垃圾回收機制-回收週期PHP
- JVM垃圾回收之三色標記JVM