2012-09-20 11 views
6

मैंने हाल ही में स्कैला के साथ खेलना शुरू कर दिया और निम्नलिखित में भाग गया। फ़ाइल की रेखाओं के माध्यम से फिर से शुरू करने के लिए 4 अलग-अलग तरीके हैं, कुछ सामान करें, और परिणाम को दूसरी फ़ाइल में लिखें। इन तरीकों में से कुछ काम करते हैं जैसा कि मैं सोचता हूं (हालांकि ऐसा करने के लिए बहुत सारी मेमोरी का उपयोग करना) और कुछ यादों को खत्म नहीं करते हैं।स्कैला इटेरेबल मेमोरी लीक्स

विचार स्केल के गेटलाइन इटरेटर को एक इटेबल के रूप में लपेटना था। मुझे परवाह नहीं है कि यह फ़ाइल को कई बार पढ़ता है - यही वह है जो मैं उम्मीद करता हूं।

class FileIterable(file: java.io.File) extends Iterable[String] { 
    override def iterator = io.Source.fromFile(file).getLines 
} 

// Iterator 

// Option 1: Direct iterator - holds at 100MB 
def lines = io.Source.fromFile(file).getLines 

// Option 2: Get iterator via method - holds at 100MB 
def lines = new FileIterable(file).iterator 

// Iterable 

// Option 3: TraversableOnce wrapper - holds at 2GB 
def lines = io.Source.fromFile(file).getLines.toIterable 

// Option 4: Iterable wrapper - leaks like a sieve 
def lines = new FileIterable(file) 

def values = lines 
     .drop(1) 
     //.map(l => l.split("\t")).map(l => l.reduceLeft(_ + "|" + _)) 
     //.filter(l => l.startsWith("*")) 

val writer = new java.io.PrintWriter(new File("out.tsv")) 
values.foreach(v => writer.println(v)) 
writer.close() 

फ़ाइल यह पढ़ रही है ~ 1 एमबी लाइनों के साथ 10GB:

यहाँ मेरी रेप्रो कोड है।

पहले दो विकल्प फ़ाइल की निरंतर मात्रा (~ 100 एमबी) का उपयोग करते हुए फ़ाइल को पुन: सक्रिय करते हैं। यही वह है जो मैं उम्मीद करता हूं। यहां नकारात्मकता यह है कि एक पुनरावर्तक का उपयोग केवल एक बार किया जा सकता है और यह स्काला के कॉल-बाय-नाम सम्मेलन का उपयोग एक psuedo-iterable के रूप में कर रहा है। (संदर्भ के लिए, समकक्ष सी # कोड ~ 14 एमबी का उपयोग करता है)

तीसरी विधि को ट्राइवरेबलऑन में परिभाषित करने योग्य कहा जाता है। यह एक काम करता है, लेकिन यह एक ही काम करने के लिए लगभग 2 जीबी का उपयोग करता है। कोई विचार नहीं कि स्मृति कहां जा रही है क्योंकि यह पूरे इटेबल को कैश नहीं कर सकता है।

चौथा सबसे खतरनाक है - यह तुरंत सभी उपलब्ध स्मृति का उपयोग करता है और ओओएम अपवाद फेंकता है। यहां तक ​​कि वीडर यह भी है कि यह मेरे द्वारा परीक्षण किए गए सभी कार्यों के लिए करता है: ड्रॉप, मानचित्र और फ़िल्टर। कार्यान्वयन को देखते हुए, उनमें से कोई भी ज्यादा राज्य बनाए रखने के लिए प्रतीत होता है (हालांकि ड्रॉप थोड़ा संदिग्ध दिखता है - यह सिर्फ आइटमों की गणना क्यों नहीं करता है?)। अगर मैं कोई परिचालन नहीं करता, तो यह ठीक काम करता है।

मेरा अनुमान है कि कहीं भी यह प्रत्येक पंक्ति को पढ़ने के संदर्भों को बनाए रखता है, हालांकि मैं कल्पना नहीं कर सकता कि कैसे। स्कैला में इटेरबल्स पास करते समय मैंने वही स्मृति उपयोग देखा है। उदाहरण के लिए यदि मैं केस 3 (.toterable) लेता हूं और उस विधि को पास करता हूं जो एक फ़ाइल में एक इटेबल [स्ट्रिंग] लिखता है, तो मुझे वही विस्फोट दिखाई देता है।

कोई विचार?

उत्तर

6

नोट कैसे ScalaDoc of Iterable का कहना है:

इस विशेषता के

क्रियान्वयन हस्ताक्षर के साथ एक ठोस तरीका प्रदान करने की जरूरत है:

def iterator: Iterator[A] 

उन्होंने यह भी एक तरीका है newBuilder जो एक बिल्डर बनाता है प्रदान करने की आवश्यकता एक ही तरह के संग्रह के लिए।

जब से तुम newBuilder के लिए एक कार्यान्वयन प्रदान नहीं करते हैं, तो आप डिफ़ॉल्ट कार्यान्वयन है, जो एक ListBuffer का उपयोग करता है और इस प्रकार स्मृति में सब कुछ फिट करने की कोशिश करता मिलता है।

आप Iterable.drop रूप

def drop(n: Int) = iterator.drop(n).toIterable 

लागू करने के लिए चाहते हो सकता है लेकिन यह है कि संग्रह पुस्तकालय के प्रतिनिधित्व निश्चरता (यानी iterator.toIterable एक Stream रिटर्न के साथ टूट जाएगा, जब आप List.drop चाहते एक List आदि वापस जाने के लिए - इस प्रकार की जरूरत Builder अवधारणा के लिए)।

+1

दिलचस्प ... मैं सी # से आ रहा हूं जहां सभी की देखभाल की जाती है।जिज्ञासा से बाहर - वे पूरे अनुक्रम को डिफ़ॉल्ट विकल्प के रूप में क्यों बफर करना चुनेंगे? –

+0

क्या इसका यह भी अर्थ है कि जब मैं अनुक्रम को एक इटेबल [टी] पैरामीटर के रूप में पास करता हूं तो यह डिफ़ॉल्ट रूप से बफर किया जाएगा? यदि हां, तो क्या वह उद्देश्य को हराने में नहीं है? मैं इस धारणा के तहत था कि डेटा केवल स्मृति में बफर किया जाएगा जब मैं स्पष्ट रूप से इसे लिस्ट, टूएरे, आदि के माध्यम से पूछता हूं .. –

+0

मैं संग्रह पुस्तकालय के डिजाइन पर टिप्पणी करने के लिए वास्तव में योग्य नहीं हूं (मानक परिचय विषय है [यहां] (http://www.artima.com/scalazine/articles/scala_collections_architecture.html))। आप वास्तव में केवल समस्याओं में चल रहे हैं क्योंकि आप Iterable का विस्तार करने की कोशिश कर रहे हैं, आप स्ट्रीम या इटरेटर के साथ ठीक होंगे। – themel

संबंधित मुद्दे