在WeakReferences列表中,最后一项永远不会被垃圾回收

时间:2017-05-29 12:07:01

标签: java garbage-collection weak-references

使用我的服务器应用程序,我使用WeakReferences列表来保持计数并处理到服务器的活动会话。我正在运行定期gc来清理非活动会话列表,但由于某种原因,始终会保留一个引用。根据重写的finalize方法,这是最后创建的会话。

我对为什么会发生这种情况一无所知。我首先想到这可能是由于静态方法或变量,但是现在我已经从ClientHandlerThread类中删除了这些对象。服务器类没有其他引用,但弱引用列表。目前这对我来说不是一个大问题,但是为了更好地理解java如何选择垃圾收集对象将来会有用。 :)以下是最重要的代码片段:

Server.java:

public class Server {

    private List<WeakReference<ClientHandlerThread>> m_connectedClients = 
            Collections.synchronizedList(
                    new ArrayList<WeakReference<ClientHandlerThread>>());

    /** Counter to identify sessions */
    private static AtomicInteger m_NumSession = new AtomicInteger(0);

    Server() {

        SSLServerSocket sslDataTraffic = null;

        // Sockets are initialized here - code removed for clarity

        // Run periodic GC
        Thread stThread = new Thread() {
            public void run() {
                do {
                    try {
                        Thread.sleep(5000);                        
                    }
                    catch (InterruptedException ignore) {}

                    System.runFinalization();
                    System.gc();

                    cleanUpSessionsList();

                } while (true);
            }
        };

        stThread.setPriority(Thread.MIN_PRIORITY);
        stThread.start();

        // Listen to new connections, create handlers and add to list
        while (true) {          
            try {
                SSLSocket sslDataTrafficSocketInstance = 
                        (SSLSocket) sslDataTraffic.accept();

                ClientHandlerThread c = new ClientHandlerThread(
                        sslDataTrafficSocketInstance,
                        m_NumSession.incrementAndGet());

                c.start();
                m_connectedClients.add(new WeakReference<>(c));             

            }  catch (Exception e) {
                e.printStackTrace();
            }

        }
    }



    /** Clean any old references and return the number of active connections
     * @return
     */
     public int cleanUpSessionList() {
        int i = 0;

        synchronized(m_connectedClients) {
            Iterator<WeakReference<ClientHandlerThread>> it = 
                    m_connectedClients.iterator();
            while (it.hasNext()) {
                WeakReference<ClientHandlerThread> sessionRef = it.next();
                if (sessionRef.get() == null)
                    it.remove();
                else
                    i++;
            }
        }

        System.out.println("Active sessions: " + i");

        return i;
    }

}

ClientHandlerThread.java:

public class ClientHandlerThread extends Thread {

    private int m_SessionID;
    private SSLSocket dataSocket;

    public ClientHandlerThread(
            SSLSocket dataSocket,
            int sessionID) {

        this.dataSocket = dataSocket;
        m_SessionID = sessionID;

    }



    public void run() {
        // code removed
    }



    @Override
    protected void finalize() throws Throwable {
        System.out.println("Session " + m_SessionID + " finalized");
        super.finalize();
    }

}

2 个答案:

答案 0 :(得分:0)

那是关于所有错误的(代码本身并不坏,但你做了很多我通常会避免的事情)。

  • 使用In [14]: from itertools import product In [15]: pd.DataFrame(list(product(df.name, df.number)), columns=df.columns) Out[15]: name number 0 a 2 1 a 1 2 b 2 3 b 1 代替ReferenceQueue
  • 考虑使用finalize代替弱,因为AFAICT不需要访问裁判。
  • 如果您想要的只是计算活动会话,只需计算它们(通过会话跟踪代码包围处理程序代码)。
  • 您应该使用线程池。
  • 运行定期GC会影响性能(虽然它甚至可能有助于提高性能,但您不应该依赖它)。

关于问题本身......不知道,但代码中可能存在阻止最后一个帖子被释放的东西。如已经建议的那样,执行堆快照,通过内存分析器运行它。

答案 1 :(得分:0)

我发布了related question后发现了这个问题作为交叉参考。

我没有关于为什么发生的答案,我认为应该没有,但是我可以告诉您什么我认为正在发生,并且我很好奇建议的解决方法是否会改变您所看到的行为。 (如果您仍然有代码,我知道这是一个老问题了。)

据我所知,JRE某种程度上维护了对最后创建的作用域变量的引用。通过将变量设置为null(或创建另一个新的,不相关的作用域变量),此问题就消失了。

是这样的:

ClientHandlerThread c = new ClientHandlerThread(
        sslDataTrafficSocketInstance,
        m_NumSession.incrementAndGet());

c.start();
m_connectedClients.add(new WeakReference<>(c)); 
c = null;           

同样,我并不是说应该这样运行,而是从我在类似情况下所做的测试中得出的结论。