क्या किसी के पास यह उदाहरण है कि यह कैसे करें? या वे कचरा कलेक्टर द्वारा संभाला जाता है? im tomcat6थ्रेडलोकल्स को कैसे साफ़ करें
उत्तर
जावाडोक का उपयोग कर इस का कहना है:
"प्रत्येक धागा जब तक एक धागे की स्थानीय चर का उसकी प्रतिलिपि में एक अंतर्निहित संदर्भ रखती है के रूप में धागा जीवित है और ThreadLocal उदाहरण पहुँचा जा सकता है, एक के बाद धागा दूर चला जाता है, धागे की स्थानीय उदाहरणों के अपने प्रतियां के सभी कचरा संग्रहण के अधीन हैं (जब तक इन प्रतियों को अन्य संदर्भ मौजूद हैं)।
आपके आवेदन या (आप अनुरोध धागे के बारे में बात कर रहे हैं) कंटेनर एक का उपयोग करता है थ्रेड पूल का मतलब है कि धागे मर नहीं जाते हैं। यदि आवश्यक हो, तो आपको निपटने की आवश्यकता होगी धागा खुद को स्थानीय बनाता है। ऐसा करने का एकमात्र साफ तरीका ThreadLocal.remove()
विधि को कॉल करना है।
इसके दो कारण आप एक थ्रेड पूल में धागे के लिए धागा स्थानीय लोगों को साफ करने के लिए चाहते हो सकता है:
- स्मृति को रोकने के (या परिकल्पित संसाधन) को लीक, या
- से जानकारी के आकस्मिक रिसाव को रोकने के धागे स्थानीय लोगों के माध्यम से एक दूसरे के लिए एक अनुरोध।
थ्रेड स्थानीय मेमोरी लीक आमतौर पर बाध्य थ्रेड पूल के साथ एक प्रमुख मुद्दा नहीं होना चाहिए क्योंकि किसी भी धागे के स्थानीय लोगों को अंततः अधिलेखित होने की संभावना है; यानी जब धागा का पुन: उपयोग किया जाता है। हालांकि, यदि आप एक नया ThreadLocal
उदाहरण बार-बार बनाते हैं (static
चर का उपयोग करने के बजाय एक सिंगलटन उदाहरण धारण करने के बजाय), थ्रेड स्थानीय मान ओवरराइट नहीं होंगे, और प्रत्येक थ्रेड के threadlocals
मानचित्र में जमा होंगे। इसका परिणाम गंभीर रिसाव हो सकता है।
अपने वेब ऐप्लिकेशन की ServletContext
के साथ एक ServletRequestListener
रजिस्टर और लागू करने के लिए यह मानते हुए कि आप धागा स्थानीय लोगों बनाए जाते हैं/धागा स्थानीय लीक से बचने के लिए एक HTTP अनुरोध की एक वेब ऐप्लिकेशन की प्रोसेसिंग, तो एक ही रास्ता के दौरान इस्तेमाल के बारे में बात कर रहे हैं है मौजूदा धागे के लिए थ्रेड स्थानीय को साफ़ करने के लिए श्रोता की requestDestroyed
विधि।
ध्यान दें कि इस संदर्भ में आपको जानकारी एक अनुरोध से दूसरे अनुरोध पर लीक होने की संभावना पर विचार करने की आवश्यकता है।
धागा है कि पहली जगह (- नहीं कार्यकर्ता धागे के साथ मामला या धागा कचरा एकत्र हो) में वहाँ में डाल भीतर से छोड़कर ThreadLocal
मूल्यों सफाई करने के लिए कोई तरीका नहीं है। इसका मतलब है कि जब आप एक सर्वलेट अनुरोध समाप्त हो जाते हैं (या AsyncContext को Servlet 3 में किसी अन्य थ्रेड में स्थानांतरित करने से पहले) को अपने थ्रेडलोकल को साफ़ करने के लिए सावधानी बरतनी चाहिए, क्योंकि उस बिंदु के बाद आपको कभी भी उस विशिष्ट कार्यकर्ता थ्रेड को दर्ज करने का मौका नहीं मिल सकता है, और इसलिए, जब सर्वर पुनरारंभ नहीं होता है तो आपका वेब ऐप बेरोजगार होता है जब परिस्थितियों में स्मृति को रिसाव कर देगा।
इस तरह के सफाई करने के लिए एक अच्छी जगह ServletRequestListener.requestDestroyed() है।
आप वसंत का उपयोग करते हैं, सभी आवश्यक तारों जगह में पहले से ही है, तो आप बस (जो अपने आप होता है) उन्हें सफाई के बारे में चिंता किए बिना अपने अनुरोध दायरे में सामान रख सकते हैं:
RequestContextHolder.getRequestAttributes().setAttribute("myAttr", myAttr, RequestAttributes.SCOPE_REQUEST);
. . .
RequestContextHolder.getRequestAttributes().getAttribute("myAttr", RequestAttributes.SCOPE_REQUEST);
यहाँ कुछ कोड है जब आप वास्तविक थ्रेड स्थानीय चर का संदर्भ नहीं रखते हैं तो वर्तमान थ्रेड से सभी थ्रेड स्थानीय चर साफ़ करने के लिए। तुम भी अन्य थ्रेड के लिए सफाई धागा स्थानीय चर के लिए यह सामान्य कर सकते हैं:
private void cleanThreadLocals() {
try {
// Get a reference to the thread locals table of the current thread
Thread thread = Thread.currentThread();
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
Object threadLocalTable = threadLocalsField.get(thread);
// Get a reference to the array holding the thread local variables inside the
// ThreadLocalMap of the current thread
Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = threadLocalMapClass.getDeclaredField("table");
tableField.setAccessible(true);
Object table = tableField.get(threadLocalTable);
// The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
// is a reference to the actual ThreadLocal variable
Field referentField = Reference.class.getDeclaredField("referent");
referentField.setAccessible(true);
for (int i=0; i < Array.getLength(table); i++) {
// Each entry in the table array of ThreadLocalMap is an Entry object
// representing the thread local reference and its value
Object entry = Array.get(table, i);
if (entry != null) {
// Get a reference to the thread local object and remove it from the table
ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
threadLocal.remove();
}
}
} catch(Exception e) {
// We will tolerate an exception here and just log it
throw new IllegalStateException(e);
}
}
कभी-कभी यह एकमात्र तरीका है .. या धागे का पुन: उपयोग न करें। – vlad
यह थ्रेड के लिए हर किसी के वैश्विक चर को हटा देता है, जो मुझे जावा 6 में थोड़ा सा करता है। 'Java.util.concurrent.locks.ReentrantReadWriteLock' कभी-कभी 'अवैध मॉनिटरस्टेट अपवाद' फेंक देगा क्योंकि हमने अपना कैश होल्ड काउंटर हटा दिया था, इसलिए इसे 0 से नीचे कम करने की कोशिश की गई यह विशेष मुद्दा जावा 8 में नहीं होता है, लेकिन कौन जानता है कि अन्य थ्रेडलोकल्स इस पर प्रतिक्रिया कैसे करते हैं, जैसे स्प्रिंग कनेक्शन या log4j MDC। – Noumenon
क्या हमें इस वेबैप के क्लासलोडर द्वारा थ्रेडलोकल मान लोड करने की आवश्यकता है? threadLocal.get()। GetClass()। GetClassLoader() == थ्रेड.current थ्रेड()। GetContextClassLoader() – hurelhuyag
मैं इस सवाल भले ही यह पुरानी करने के लिए मेरा उत्तर योगदान करना चाहते हैं। मैं एक ही समस्या से पीड़ित था (जीसन थ्रेडलोकल अनुरोध थ्रेड से नहीं हटाया जा रहा था), और कभी भी सर्वर को फिर से शुरू करने में सहज महसूस किया था जब यह स्मृति से बाहर चला गया (जो बड़ा समय बेकार !!)।
देव मोड पर सेट किए गए जावा वेब ऐप के संदर्भ में (जिसमें सर्वर हर बार बाउंस करने के लिए सेट होता है, यह कोड में बदलाव को महसूस करता है, और संभवतः डीबग मोड में भी चल रहा है), मैंने जल्दी से सीखा थ्रेडलोकल्स भयानक हो सकते हैं और कभी-कभी दर्द हो सकते हैं। मैं प्रत्येक अनुरोध के लिए थ्रेडलोकल आमंत्रण का उपयोग कर रहा था। आमंत्रण के अंदर। मैं कभी-कभी मेरी प्रतिक्रिया उत्पन्न करने के लिए जीसन का भी उपयोग करता हूं। मैं फ़िल्टर में 'कोशिश' ब्लॉक के अंदर आमंत्रण लपेटूंगा, और इसे 'अंत में' ब्लॉक के अंदर नष्ट कर दूंगा।
मैंने जो देखा (मैंने अभी तक इसे वापस करने के लिए मीट्रिक नहीं किया है) यह है कि अगर मैंने कई फाइलों में बदलाव किए हैं और सर्वर लगातार मेरे परिवर्तनों के बीच उछाल रहा है, तो मैं अधीर हो जाऊंगा और सर्वर को पुनरारंभ करूंगा (टोमकैट आईडीई से सटीक होना)। सबसे अधिक संभावना नहीं है, मैं 'आउट ऑफ़ मेमोरी' अपवाद के साथ समाप्त होगा।
मेरे आस-पास एक ServletRequestListener कार्यान्वयन को शामिल करने के लिए मुझे यह कैसे मिला, और मेरी समस्या गायब हो गई। मुझे लगता है कि क्या हो रहा था कि अनुरोध के बीच में, यदि सर्वर कई बार उछाल लेगा, तो मेरे थ्रेडलोकल्स को साफ़ नहीं किया जा रहा था (जीएसन शामिल) इसलिए मुझे थ्रेडलोकल्स और दो या तीन चेतावनी के बारे में यह चेतावनी मिल जाएगी, सर्वर दुर्घटनाग्रस्त हो जाएगा। ServletResponseListener स्पष्ट रूप से मेरे थ्रेडलोकल्स को बंद करने के साथ, जीसन समस्या गायब हो गई।
मुझे उम्मीद है कि यह समझ में आता है और आपको थ्रेडलोकल मुद्दों को दूर करने का विचार देता है। उन्हें अपने उपयोग के बिंदु के आसपास हमेशा बंद करें। ServletRequestListener में, प्रत्येक थ्रेडलोकल रैपर का परीक्षण करें, और यदि उसके पास अभी भी किसी ऑब्जेक्ट का मान्य संदर्भ है, तो उस बिंदु पर इसे नष्ट कर दें।
मुझे यह भी इंगित करना चाहिए कि इसे कक्षा के अंदर स्थिर चर के रूप में थ्रेडलोकल लपेटने की आदत बनाते हैं। इस तरह आपको गारंटी दी जा सकती है कि ServeltRequestListener में इसे नष्ट करके, आपको उसी कक्षा के अन्य उदाहरणों के बारे में चिंता करने की आवश्यकता नहीं होगी।
JVM स्वचालित रूप से थ्रेडलोकल ऑब्जेक्ट के भीतर मौजूद सभी संदर्भ-कम ऑब्जेक्ट्स को साफ़ कर देगा।
उन वस्तुओं को साफ करने का एक और तरीका (उदाहरण के लिए कहें, ये ऑब्जेक्ट्स सभी थ्रेड असुरक्षित ऑब्जेक्ट्स हो सकती हैं जो उन्हें मौजूद हैं) उन्हें कुछ ऑब्जेक्ट होल्डर क्लास के अंदर रखना है, जो मूल रूप से इसे रखता है और आप अंतिम विधि को ओवरराइड कर सकते हैं उस वस्तु को साफ करने के लिए जो इसके अंदर रहता है। फिर यह कचरा कलेक्टर और इसकी नीतियों पर निर्भर करता है, जब यह finalize
विधि का आह्वान करेगा।
public class MyObjectHolder {
private MyObject myObject;
public MyObjectHolder(MyObject myObj) {
myObject = myObj;
}
public MyObject getMyObject() {
return myObject;
}
protected void finalize() throws Throwable {
myObject.cleanItUp();
}
}
public class SomeOtherClass {
static ThreadLocal<MyObjectHolder> threadLocal = new ThreadLocal<MyObjectHolder>();
.
.
.
}
फिर जावाडोक प्रलेखन ध्यान से पढ़ना: के रूप में लंबे धागे के रूप में
'प्रत्येक धागा एक धागे की स्थानीय चर का उसकी प्रतिलिपि में एक अंतर्निहित संदर्भ रखती
यहाँ एक कोड नमूना है जीवित और थ्रेडलोकल उदाहरण सुलभ है; धागे दूर जाने के बाद, थ्रेड-स्थानीय उदाहरणों की इसकी सभी प्रतियां कचरा संग्रह के अधीन होती हैं (जब तक कि इन प्रतियों के अन्य संदर्भ मौजूद न हों)। '
कुछ भी साफ करने की आवश्यकता नहीं है, रिसाव के लिए एक' और 'स्थिति है। तो यहां तक कि एक वेब कंटेनर में जहां थ्रेड एप्लिकेशन तक जीवित रहता है, जब तक वेबपैप क्लास अनलोड हो जाता है (केवल मूल वर्ग लोडर में लोड की गई स्थिर कक्षा में बीइंग संदर्भ इसे रोक देगा और इसका थ्रेडलोकल के साथ कुछ लेना देना नहीं है लेकिन सामान्य समस्याएं स्थैतिक डेटा के साथ साझा जार के साथ) और स्थिति का दूसरा चरण अब और नहीं मिला है, इसलिए थ्रेड स्थानीय प्रति कचरा संग्रह के लिए योग्य है।
थ्रेड स्थानीय मेमोरी लीक का कारण नहीं हो सकता है, जहां तक कार्यान्वयन दस्तावेज़ीकरण को पूरा करता है।
उत्तर के लिए धन्यवाद। समस्या यह है कि अनुरोध के साथ किए जाने के बाद मैं केवल थ्रेडलोकल को हटा सकता हूं। और अनुरोध के साथ किए जाने पर मुझे जानने का कोई आसान तरीका नहीं है। जिस तरह से मैं कर रहा हूं, मेरे पास अनुरोध की शुरुआत में एक इंटरसेप्टर है जो थ्रेडलोकल सेट करता है (यह एक स्थैतिक) है। इसलिए मैं प्रत्येक अनुरोध की शुरुआत में इसे रीसेट करता हूं ... – Ricardo
यदि थ्रेडलोकल ऑब्जेक्ट एक स्थिर है, तो रिसाव अधिक प्रबंधनीय समस्या है; यानी आप थ्रेड पूल में प्रति थ्रेड केवल एक उदाहरण (एक थ्रेड स्थानीय मान) लीक करते हैं। स्थानीय मूल्यों के थ्रेड के आधार पर, यह रिसाव परेशान करने योग्य नहीं हो सकता है। –
यह मूल प्रश्न का उत्तर नहीं देता है। मेरी राय में सवाल यह है कि मेमोरी लीक को रोकने के लिए अनावश्यकता पर वेब ऐप में थ्रेडलोकल्स को कैसे साफ किया जाए। एक स्थिर थ्रेडलोकल का उपयोग इस मामले में पर्याप्त नहीं है, क्योंकि एक पुनर्वित्तित ऐप किसी अन्य वर्ग लोडर में होगा। – rustyx