Soru Threadlocals nasıl temizlenir


Bunların nasıl yapılabileceği bir örneği var mı? Yoksa çöp toplayıcı tarafından mı ele alınır? tomcat6 kullanarak im


46
2017-10-06 01:57


Menşei




Cevaplar:


Javadoc şöyle diyor:

"Her iş parçacığı, iş parçacığı olduğu sürece ve ThreadLocal örneğine erişilebildiği sürece iş parçacığı yerel değişkeninin kopyasına örtük bir başvuruda bulunur. Bir iş parçacığı kaybolduğunda, iş parçacığı yerel örneklerinin tüm kopyaları çöp koleksiyonuna tabidir. (Bu kopyalara başka referanslar olmadıkça).

Uygulamanız veya (istek dizileri hakkında konuşuyorsanız) kapsayıcı, iş parçacığı ölmediği anlamına gelen bir iş parçacığı havuzu kullanır. Gerekirse, iş parçacığıyla kendiniz ilgilenmeniz gerekir. Bunu yapmanın tek temiz yolu aramaktır. ThreadLocal.remove() yöntem.

Bir iş parçacığı havuzundaki iş parçacığı için iş parçacığı öğelerini temizlemek isteyebileceğiniz iki neden vardır:

  • belleğin (veya varsayımsal kaynakların) sızmasını önlemek için, veya
  • Bir bilgiden kazara sızan bilginin iş parçacığı aracılığıyla bir başka istekten kaçmasını önlemek.

İş parçacığı yerel bellek sızıntıları normalde sınırlı sayıda iş parçacığı havuzuyla ilgili önemli bir sorun olmamalıdır. yani iplik yeniden kullanıldığında. Ancak, yeni bir yaratma hatasını yaparsanız ThreadLocal tekrar tekrar örnekleri ( static singleton örneğini tutmak için değişken), iş parçacığı yerel değerleri üzerine yazılmaz ve her iş parçacığında birikir threadlocals harita. Bu ciddi bir sızıntıya neden olabilir.


Bir web sunucusunun bir HTTP isteğinin işlenmesi sırasında oluşturulan / kullanılan iş parçacığı hakkında konuştuğunuzu varsayarsak, iş parçacığı yerel sızıntılarını önlemenin bir yolu, ServletRequestListener senin webapp ile ServletContext ve dinleyiciyi uygulamak requestDestroyed Geçerli iş parçacığı için iş parçacığı öğelerini temizleme yöntemi.

Bu bağlamda olasılığını da göz önünde bulundurmanız gerektiğini unutmayın. bilgi bir istekten diğerine sızmak.


53
2017-10-06 03:02



Cevap için teşekkürler. Sorun şu ki, istekte bulunulduğunda sadece threadlocal'ı kaldırabilirim. ve isteğimle ne zaman yapıldığını bilmenin kolay bir yolu yok. Yaptığım şey, threadlocal'ı (onun bir statik) ayarlayan isteğin başında bir engelleyiciyim. bu yüzden her isteğin başında sıfırladım ... - Ricardo
Threadlocal nesne statik ise, sızıntı daha yönetilebilir bir sorundur; Yani, yalnızca iş parçacığı havuzunda iş parçacığı başına bir örneğe (bir iş parçacığı yerel değerine) kadar sızıntı yaparsınız. Yerel değerlerin ne olduğuna bağlı olarak, bu sızıntı rahatsız edilmeye değer olmayabilir. - Stephen C
StatLite ThreadLocal kullanıyor olsanız bile, değeriniz aynı sınıf yükleyici tarafından yüklenen bir sınıfa başvurursa, web sunucunuzu yeniden dağıttığınızda bir sınıf yükleyici sızıntısına sahip olabilirsiniz. Çift kaşlılık başlatmayı kullanırsanız, bu anonim bir sınıf oluşturacağından oluşabilir. Bunun için bir düzeltme oluşturdum: github.com/codesinthedark/ImprovedThreadLocal - user1944408


Gerçek iş parçacığı yerel değişkenine bir başvuru olmadığında, geçerli iş parçacığındaki tüm iş parçacığı yerel değişkenlerini temizlemek için bazı kodlar. Ayrıca, diğer iş parçacıklarının yerel değişkenleri temizlemek için de genelleştirebilirsiniz:

    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);
        }
    }

31
2018-05-20 07:17



Bazen bu tek yol ... ya da ipleri tekrar kullanmayın. - vlad
Bu, Java 6'da beni ısırdıran iş parçacığı için herkesin genel değişkenlerini siler. java.util.concurrent.locks.ReentrantReadWriteLock arada sırada bir IllegalMonitorStateException cachedHoldCounter öğesini sildiğimiz için, 0'ın altına düşürmeye çalıştık. Bu sorun, Java 8'de gerçekleşmez, ancak diğer ThreadLocal'ın Spring bağlantıları veya log4j MDC gibi nasıl tepki vereceğini bilir. - Noumenon
ThreadLocal değerinin bu webapp'ın sınıf yükleyicisi tarafından yüklendiğini kontrol etmemiz gerekiyor mu? threadLocal.get (). getClass (). getClassLoader () == Thread.currentThread (). getContextClassLoader () - hurelhuyag


Temizleme yapmanın bir yolu yok ThreadLocal içindeki değerler hariç onları ilk etapta yerleştiren iş parçacığı (veya iş parçacığı çöp toplandığında - iş parçacığı ile durum). Bu, bir servlet isteği tamamlandığında (veya AsyncContext'i Servlet 3'teki başka bir iş parçacığına aktarmadan önce) ThreadLocal öğelerinizi temizlemeye dikkat etmeniz gerektiği anlamına gelir, çünkü bu noktadan sonra o belirli çalışan iş parçacığına girme şansınız olmayacaktır. Sunucu yeniden başlatılmamışken web uygulamanızın yeniden dağıtılmadığı durumlarda belleği kaçıracak.

Böyle bir temizlik yapmak için iyi bir yer ServletRequestListener.requestDestroyed ().

Eğer yayı kullanırsanız, gerekli tüm kablo bağlantıları zaten mevcutsa, temizlik kapsamı hakkında endişelenmeksizin isteklerinizi kapsam içine alabilir (otomatik olarak gerçekleşir):

RequestContextHolder.getRequestAttributes().setAttribute("myAttr", myAttr, RequestAttributes.SCOPE_REQUEST);
. . .
RequestContextHolder.getRequestAttributes().getAttribute("myAttr", RequestAttributes.SCOPE_REQUEST);

12
2018-01-25 15:07





Tekrar Javadoc belgelerini tekrar okumak:

'Her iş parçacığı, iş parçacığı canlı olduğu ve ThreadLocal örneğinin erişilebilir olduğu sürece iş parçacığı yerel değişkeninin kopyasına örtük bir başvuruda bulunur; Bir iş parçacığı ortadan kalktıktan sonra, iş parçacığı yerel örneklerinin tüm kopyaları çöp toplamaya tabidir (bu kopyalara başka referanslar olmadıkça). '

Bir şeyi temizlemeye gerek yok, hayatta kalmak için sızıntı için bir 'VE' koşulu var. Dolayısıyla, iş parçacığı uygulamanın uygulandığı bir web kapsayıcısında bile, webapp sınıfı kaldırıldığı sürece (üst sınıf yükleyicide yüklenen statik bir sınıfta yalnızca birleştirme başvurusu bunu engelleyecektir ve bunun ThreadLocal ile değil, statik verilerle paylaşılan kavanozlarla genel sorunları vardır) daha sonra AND'nin ikinci ayağı koşul artık karşılanmamaktadır, bu nedenle iş parçacığı yerel kopyası çöp toplama için uygundur.

Uygulama iş parçacığı belgelere uygun olduğu için iş parçacığı yerel bellek sızıntılarının nedeni olamaz.


1
2018-01-01 13:38





Eski olmasına rağmen bu soruya verdiğim cevaplara katkıda bulunmak istiyorum. Ben aynı sorun (gson threadlocal isteği iş parçacığından kaldırılmıyor) tarafından sorulmuştu ve hatta bellek (her zaman büyük zaman berbat!) Bitti zaman sunucu yeniden başlatmayı rahatlattı.

Dev moduna ayarlanmış bir java web uygulaması bağlamında (sunucu, kodda bir değişiklik algıladığında ve muhtemelen hata ayıklama modunda çalışırken her seferinde geri dönecek şekilde ayarlanmıştır), threadlocals'ın harika olabileceğini çabucak öğrendim ve bazen bir acı olur. Her istek için bir threadlocal Invocation kullanıyordum. Çağrının İçinde. Bazen yanıtımı üretmek için gson kullanabilirim. Invocation'ı filtredeki bir 'try' bloğuna sardım ve onu 'nihayet' bloğun içinde yok edecektim.

Gözlemlediklerim (bunu şu ana kadar destekledim), birkaç dosyada değişiklik yaparsam ve sunucum sürekli değişimlerim arasında sıçrarsa, sabırsızlaşır ve sunucuyu yeniden başlatırdım (tomcat tam olarak) IDE'den. Muhtemelen, 'Bellek yetersiz' istisnası ile sonuçlanırdı.

Bu konuyla ilgili olarak benim app nasıl bir ServletRequestListener uygulama eklemek ve benim sorunum kayboldu. Sanırım, bir isteğin ortasında, sunucunun birkaç kez geri dönmesi halinde, threadlocals'ım temizlenemedi (gson dahil), bu yüzden threadlocals ile ilgili uyarı alırım ve iki veya üç uyarı daha sonra, Sunucu çökecektir. ServletResponseListener, threadlocals'ımı açık bir şekilde kapatırken, gson sorunu ortadan kalktı.

Umarım bu mantıklıdır ve size threadlocal sorunların üstesinden nasıl geleceğine dair bir fikir verir. Bunları her zaman kullanım noktaları etrafında kapatın. ServletRequestListener'de, her bir threadlocal sarıcıyı sınayın ve hala bazı nesnelere geçerli bir başvurusu varsa, o noktada yok edin.

Ayrıca, bir threadlocal'ı bir sınıfa statik bir değişken olarak sarmanın bir alışkanlık haline getirdiğine de dikkat etmeliyim. Bu şekilde ServeltRequestListener'da yok ederek, aynı sınıfın etrafına takılan diğer örneklerden endişelenmenize gerek kalmayacağı garanti edilebilir.


0
2018-06-05 06:45





JVM, ThreadLocal nesnesinde bulunan tüm referanssız nesneleri otomatik olarak temizler.

Bu nesneleri temizlemenin bir başka yolu (örneğin, bu nesneler, etrafta bulunan tüm güvenli olmayan nesneler olabilir) onları temelde tutan bazı Nesne Tutucu sınıfının içine koymaktır ve nesneyi temizlemek için sonlandırma yöntemini geçersiz kılabilirsiniz bunun içinde yer alır. Yine Çöp Toplayıcısına ve onun politikalarına, ne zaman finalize yöntem.

İşte bir kod örneği:

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>();
    .
    .
    .
}

0
2018-04-10 03:51