Swing JTextPane实现消息控制台时出现严重内存泄漏求助
Hey there, let's dig into your memory leak issue with JTextPane. I've looked over your code and spotted a few key problems that are likely causing the memory bloat. Let's break them down and fix them step by step.
Key Issues in Your Code
Incorrect Manual Tracking of Document Length and Rows
You're hardcoding the delete length to100000and manually maintainingm_docLengthandm_totalRowsNum, which leads to mismatches between your tracked values and the actual state of theStyledDocument. When you delete content, you aren't subtracting the correct number of rows fromm_totalRowsNum, and the fixed delete length doesn't match the actual length of 1000 lines of yourlineText. This means old content isn't being properly removed, and your insertions end up appending to a document that still holds leftover text.Potential Caret-Related Leaks
As you noted in the related question, repeated calls tosetCaretPosition()can cause memory leaks due to view caching. Manually forcing the caret to the end every time isn't just inefficient—it's risky. Instead, we can configure the caret to automatically stay at the end when new content is added.
Fixed Code Implementation
Here's the revised version of your code with these issues addressed:
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultCaret; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; public class ConsoleTester { private int m_totalRowsNum = 0; StyledDocument m_document; String lineText = "ONCE upon a time there were four little Rabbits, and their names were— Flopsy, Mopsy, Cotton-tail, \n"; Style styleGreen; // Removed manual m_docLength tracking; we'll use document's actual length instead void createAndShowGUI() { JFrame frame; JTextPane textPane; JTextField textField; JScrollPane scrollPane; frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); textPane = new JTextPane(); textPane.setEditable(false); textPane.setFont(new Font("Courier New", Font.PLAIN, 16)); textPane.setOpaque(false); m_document = textPane.getStyledDocument(); // Configure caret to auto-scroll to end DefaultCaret caret = (DefaultCaret) textPane.getCaret(); caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); styleGreen = textPane.addStyle("styleGreen", null); StyleConstants.setForeground(styleGreen, Color.green); StyleConstants.setBold(styleGreen, true); textField = new JTextField(); scrollPane = new JScrollPane(textPane); scrollPane.setOpaque(false); scrollPane.getViewport().setOpaque(false); frame.add(textField, BorderLayout.SOUTH); frame.add(scrollPane, BorderLayout.CENTER); frame.getContentPane().setBackground(new Color(50, 50, 50)); frame.setSize(660, 350); frame.setLocationRelativeTo(null); frame.setVisible(true); javax.swing.Timer timer = new Timer(500, new ActionListener() { public void actionPerformed(ActionEvent evt) { System.out.println("document length: " + m_document.getLength() + " m_totalRowsNum: " + m_totalRowsNum); for (int i = 0; i < 100; i++) { AddLine(); } // No need for manual setCaretPosition now } }); timer.start(); } private void AddLine() { m_totalRowsNum++; try { // When we hit 10000 rows, delete the first 1000 rows if (m_totalRowsNum >= 10000) { int rowsToDelete = 1000; int deleteLength = rowsToDelete * lineText.length(); // Ensure we don't delete more than the document actually has if (deleteLength > m_document.getLength()) { deleteLength = m_document.getLength(); } m_document.remove(0, deleteLength); m_totalRowsNum -= rowsToDelete; } // Insert at the actual end of the document m_document.insertString(m_document.getLength(), lineText, styleGreen); } catch (BadLocationException e) { e.printStackTrace(); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new ConsoleTester().createAndShowGUI(); } }); } }
What Changed & Why
- Removed manual document length tracking: Instead of relying on
m_docLength, we usem_document.getLength()to get the real-time length of the document. This eliminates mismatches that caused leftover content to stay in memory. - Fixed delete logic: We calculate the exact length of 1000 lines using
1000 * lineText.length()and updatem_totalRowsNumby subtracting the deleted rows. This ensures old content is fully removed from the document. - Auto-updating caret: By setting
DefaultCaret.ALWAYS_UPDATE, the caret automatically moves to the end when new content is added. This avoids the potential memory leaks associated with repeatedsetCaretPosition()calls and simplifies the code.
These changes should resolve the memory leak by ensuring old content is properly cleaned up and reducing unnecessary view-related overhead.
内容的提问来源于stack exchange,提问作者user4212919

