2013-09-23 9 views
6

का कारण बनने के लिए लंबे समय तक रहने वाले तारों से कैसे बचें मेरे पास एक ऐसा एप्लिकेशन है जहां मैं सर्कुलर बफर में लॉग स्ट्रिंग रखता हूं। जब एक लॉग भर जाता है, हर नए सम्मिलन के लिए, पुरानी तार कचरा संग्रह के लिए जारी की जाएगी और फिर वे पीढ़ी 2 मेमोरी में हैं। इस प्रकार, अंततः एक पीढ़ी 2 जीसी होगा, जिसे मैं टालना चाहूंगा।पीढ़ी 2 कचरा संग्रह

मैंने स्ट्रिंग को स्ट्रिंग में मार्शल करने की कोशिश की। हैरानी की बात है, मुझे अभी भी पीढ़ी 2 जीसी मिलती है: एस। ऐसा लगता है कि संरचना अभी भी स्ट्रिंग के कुछ संदर्भ रखती है। नीचे पूरा कंसोल ऐप। किसी भी मदद की सराहना की।

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Threading.Tasks; 

namespace ConsoleApplication 
{ 
    class Program 
    { 

     [StructLayout(LayoutKind.Sequential)] 
     public struct FixedString 
     { 
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 
      private string str; 

      public FixedString(string str) 
      { 
       this.str = str; 
      } 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct UTF8PackedString 
     { 
      private int length; 

      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] 
      private byte[] str; 

      public UTF8PackedString(int length) 
      { 
       this.length = length; 
       str = new byte[length]; 
      } 

      public static implicit operator UTF8PackedString(string str) 
      { 
       var obj = new UTF8PackedString(Encoding.UTF8.GetByteCount(str)); 
       var bytes = Encoding.UTF8.GetBytes(str); 
       Array.Copy(bytes, obj.str, obj.length); 
       return obj; 
      } 
     } 

     const int BufferSize = 1000000; 
     const int LoopCount = 10000000; 

     static void Main(string[] args) 
     { 
      Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", 
       "Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)"); 
      Console.WriteLine(); 
      for (int i = 0; i < 5; i++) 
      { 
       TestPerformance<string>(s => s); 
       TestPerformance<FixedString>(s => new FixedString(s)); 
       TestPerformance<UTF8PackedString>(s => s); 
       Console.WriteLine(); 
      } 
      Console.ReadKey(); 
     } 

     private static void TestPerformance<T>(Func<string, T> func) 
     { 
      var buffer = new T[BufferSize]; 
      GC.Collect(2); 
      Stopwatch stopWatch = new Stopwatch(); 
      var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) }; 
      stopWatch.Reset(); 
      stopWatch.Start(); 
      for (int i = 0; i < LoopCount; i++) 
       buffer[i % BufferSize] = func(i.ToString()); 
      stopWatch.Stop(); 
      Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", 
       typeof(T).Name.PadRight(20), 
       stopWatch.ElapsedMilliseconds, 
       (GC.CollectionCount(0) - initialCollectionCounts[0]), 
       (GC.CollectionCount(1) - initialCollectionCounts[1]), 
       (GC.CollectionCount(2) - initialCollectionCounts[2]) 
      ); 
     } 
    } 
} 

संपादित करें: UnsafeFixedString के साथ अपडेट किया कोड कि आवश्यक काम करता है: अपने कंप्यूटर पर

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Threading.Tasks; 

namespace ConsoleApplication 
{ 
    class Program 
    { 
     public unsafe struct UnsafeFixedString 
     { 
      private int length; 

      private fixed char str[256]; 

      public UnsafeFixedString(int length) 
      { 
       this.length = length; 
      } 

      public static implicit operator UnsafeFixedString(string str) 
      { 
       var obj = new UnsafeFixedString(str.Length); 
       for (int i = 0; i < str.Length; i++) 
        obj.str[i] = str[i];     
       return obj; 
      } 
     } 

     const int BufferSize = 1000000; 
     const int LoopCount = 10000000; 

     static void Main(string[] args) 
     { 
      Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", 
       "Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)"); 
      Console.WriteLine(); 
      for (int i = 0; i < 5; i++) 
      { 
       TestPerformance(s => s); 
       TestPerformance<UnsafeFixedString>(s => s); 
       Console.WriteLine(); 
      } 
      Console.ReadKey(); 
     } 

     private static void TestPerformance<T>(Func<string, T> func) 
     { 
      var buffer = new T[BufferSize]; 
      GC.Collect(2); 
      Stopwatch stopWatch = new Stopwatch(); 
      var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) }; 
      stopWatch.Reset(); 
      stopWatch.Start(); 
      for (int i = 0; i < LoopCount; i++) 
       buffer[i % BufferSize] = func(String.Format("{0}", i)); 
      stopWatch.Stop(); 
      Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", 
       typeof(T).Name.PadRight(20), 
       stopWatch.ElapsedMilliseconds, 
       (GC.CollectionCount(0) - initialCollectionCounts[0]), 
       (GC.CollectionCount(1) - initialCollectionCounts[1]), 
       (GC.CollectionCount(2) - initialCollectionCounts[2]) 
      ); 
     } 
    } 
} 

आउटपुट है:

Type     Time GC(0) GC(1) GC(2) 

String     5746 160  71  19 
UnsafeFixedString  5345 418  0  0 
+0

आप पहले स्थान पर पीढ़ी 2 कचरा संग्रहण से बचना क्यों चाहते हैं? – PVitt

+0

एप्लिकेशन में कुछ वास्तविक समय की आवश्यकताएं हैं। एक जीसी (2) एक पूर्ण फ्रीज का कारण बनता है। –

+1

क्या होगा यदि आप संदेश बाइट [] (encoding.utf8.getbytes के रूप में() तार के बजाय स्टोर –

उत्तर

7

यह एक आश्चर्य नहीं होना चाहिए कि एक struct एक साथ string फ़ील्ड यहां अंतर करता है: string फ़ील्ड हमेशा बस एक संदर्भ है प्रबंधित ढेर पर एक वस्तु को सीई - विशेष रूप से, string ऑब्जेक्ट कहीं कहीं। string अभी भी मौजूद रहेगा और अंततः जीसी 2 का कारण बन जाएगा।

यह "ठीक करने" का एकमात्र तरीका यह है कि यह पर पर ऑब्जेक्ट के रूप में नहीं है; और एक ही तरीका है कि (प्रबंधित स्मृति के बाहर पूरी तरह से जा रहा बिना) एक fixed बफर उपयोग करने के लिए है करने के लिए:

public unsafe struct FixedString 
{ 
    private fixed char str[100]; 
} 

यहाँ, हर struct उदाहरण FixedString डेटा के लिए आरक्षित 200 बाइट्स है। str बस char* पर एक रिश्तेदार ऑफसेट है जो इस आरक्षण की शुरुआत को चिह्नित करता है। हालांकि, काम कर रहे यह मुश्किल है - और पूरे unsafe कोड की आवश्यकता है। यह भी ध्यान रखें कि प्रत्येक FixedString चाहे आप वास्तव में 3 अक्षर या 170 स्टोर करना चाहते हैं, भले ही समान स्थान सुरक्षित रखें। स्मृति समस्याओं से बचने के लिए, आपको या तो नल-टेरिमिनेटर का उपयोग करने की आवश्यकता होगी, या पेलोड की लंबाई अलग से स्टोर करें।

ध्यान दें कि .NET 4.5 में, <gcAllowVeryLargeObjects> समर्थन इस तरह के मूल्यों का एक सभ्य आकार का सरणी (उदाहरण के लिए FixedString[]) संभव बनाता है - लेकिन ध्यान दें कि आप अक्सर डेटा कॉपी नहीं करना चाहते हैं। कि बचने के लिए आप हमेशा सरणी में अतिरिक्त जगह के लिए (ताकि आप पूरे सरणी कॉपी नहीं है सिर्फ एक आइटम जोड़ने के लिए), और ref के माध्यम से अलग-अलग आइटम के साथ काम करते हैं, यानी

FixedString[] data = ... 
int index = ... 
ProcessItem(ref data[index]); 

void ProcessItem(ref FixedString item) { 
    // ... 
} 

यहाँ item चाहेगा सरणी सरणी में सीधे तत्व से बात कर रहा है - हमने किसी भी समय डेटा को कॉपी नहीं किया है।

अब हमारे पास केवल ऑब्जेक्ट - सरणी स्वयं है।

+1

धन्यवाद बहुत, मार्क! बस यह काम कर रहा है। "असुरक्षित" और "निश्चित" कीवर्ड ने अंतर बनाया। मैं इसे एक उत्तर दूंगा और कोड अपडेट कर दूंगा। मुझे लगता है कि मैं अपनी सोच त्रुटि को भी स्पॉट करता हूं: मूल उदाहरण में मैंने जो एनोटेशन इस्तेमाल किया है वह सिर्फ मार्शलिंग के लिए है और यह नहीं पता कि कैसे .net स्मृति में संरचना का आयोजन करता है। –

2
const int BufferSize = 1000000; 

आपका बफर बस भी बड़े हैं, इस प्रकार लंबे समय के लिए एक स्ट्रिंग संदर्भ भंडारण और उन्हें अनुमति देने के लिए सक्षम किया जा रहा है पिछले पीढ़ी # 1 पदोन्नत होने की है।बफर आकार के साथ प्रयोग इस समाधान को प्रदान करता है:

const int BufferSize = 180000; 

कोई और जीसी (2) संग्रह नहीं।

आप इससे # 1 ढेर आकार का अनुमान लगा सकते हैं। हालांकि इस परीक्षण कार्यक्रम के लिए करना मुश्किल है, स्ट्रिंग आकार बहुत परिवर्तनीय हैं। वैसे भी असली ऐप में हैंड-ट्यूनिंग की आवश्यकता होगी।

+0

ऐप को लॉग काफी बड़ा होने की आवश्यकता है और प्रविष्टियां लंबे समय तक पीढ़ी के लिए अग्रिम बनाने के लिए काफी समय तक जीवित रहती हैं। यही वह था जिसे मैं अनुकरण करना चाहता था। धन्यवाद! –

1

हालांकि मैं मार्क Gravell और हंस Passant जवाब (हमेशा की तरह) ...

पसंद आया आप धुन आदेश एक समय पर और इसलिए फ्रीज बार से बचने के लिए जीसी ठीक कर सकते हैं। के बारे में यह here

+0

मुझे लगता है कि वर्कस्टेशन + समवर्ती वर्कस्टेशन ओएस पर डिफ़ॉल्ट मोड है और इससे मेरे कुछ धागे कुछ 100 एमएस के लिए जमा हो जाते हैं। धन्यवाद! –

0

StringBuilder एस के एक बफर का उपयोग करना अनिवार्य unsafe fixed char[] दृष्टिकोण के रूप में सटीक एक ही बात होगी पढ़ें। लेकिन यदि आप किसी विशेष स्ट्रिंग क्या आप शुरू में आवंटित परे जा रहा है की लंबाई के संभावित लचीलापन देने के (जाहिर है, हाँ, कि है कि एक स्ट्रिंग का कारण होता है, या अधिक सटीक StringBuilder के अंतर्निहित char[] कचरा संग्रहण के लिए पात्र होने के लिए, लेकिन करते हैं व्यावहारिक हो)। इसके अलावा, आपको अपनी स्ट्रिंग लम्बाई प्रबंधन करने की ज़रूरत नहीं है।

private static void TestPerformance2() 
{ 
    var buffer = new StringBuilder[BufferSize]; 
    // Initialize each item of the array. This is no different than what 
    // unsafe struct is. 
    for (int i = 0; i < BufferSize; i++) 
    { 
     buffer[i] = new StringBuilder(256); 
    } 

    GC.Collect(2); 
    Stopwatch stopWatch = new Stopwatch(); 
    var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) }; 
    stopWatch.Reset(); 
    stopWatch.Start(); 
    for (int i = 0; i < LoopCount; i++) 
    { 
     buffer[i % BufferSize].Clear(); // Or use .Length = 0;, which is what the Clear() method does internally. 

     buffer[i % BufferSize].AppendFormat("{0}", i); 
    } 
    stopWatch.Stop(); 
    Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", 
     typeof(StringBuilder).Name.PadRight(20), 
     stopWatch.ElapsedMilliseconds, 
     (GC.CollectionCount(0) - initialCollectionCounts[0]), 
     (GC.CollectionCount(1) - initialCollectionCounts[1]), 
     (GC.CollectionCount(2) - initialCollectionCounts[2]) 
    ); 
} 

और परिणाम, दोगुनी गति से (आप भी स्टॉपवॉच स्थानांतरित कर सकते हैं अप सरणी प्रारंभ शामिल करने के लिए और यह अभी भी तेजी से UnsafeFixedString से है)।

Type     Time GC(0) GC(1) GC(2) 

String     4647 131  108  23 
StringBuilder   2600 94  0  0 
UnsafeFixedString  5135 161  0  0 
संबंधित मुद्दे