java 中使用 Stringbuilder 的内存泄漏

Memory leak using Stringbuilder in java

我正在编写一个简单的程序来读取二进制文件并将其解码为字符串并输出到文本字段。为了构造字符串,我使用了 Stringbuilder。但是我注意到每次调用我的函数时,应用程序的内存使用量都会增加并且即使在函数完成后由于 Stringbuilder 对象也不会减少。 我不确定这是否是内存泄漏。 顺便说一句,我是 Java 的新手。 这是我的代码:

public class Reader {
    final StringBuilder builder = new StringBuilder();
    public void ProcessFile(String path, JTextArea area) {
        try {
            // TODO code application logic here
            final FileChannel channel;
            channel = new FileInputStream(path).getChannel();
            MappedByteBuffer buffer = enter code herechannel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
            while(buffer.remaining() > 0) {
            int totalLength = buffer.getInt();
            buffer.position(buffer.position() + 1);
            buffer.position(buffer.position() + 7);
            long timestamp = buffer.getLong();
            int protocol = buffer.get() & 0xFF;
            buffer.position(buffer.position() + 1);
            int dataLength = buffer.getInt();
            switch(protocol) {
                case 0x00:
                    //General
                    byte processorID = buffer.get();
                    byte compID = buffer.get();
                    short domainID = buffer.getShort();
                    short channelID = buffer.getShort();
                    int threadID = buffer.getInt();
                    String level = levelToString(buffer.get());
                    buffer.position(buffer.position() + 1);
                    short textLength = buffer.getShort();
                    byte[] textArray = new byte[textLength];
                    buffer.get(textArray, 0, textLength);
                    String text = new String(textArray);
                    builder.append(timestamp)
                           .append(", general, ")
                           .append(dataLength)
                           .append(", ")
                           .append(String.format("0x%02X",processorID))
                           .append(", ")
                           .append(String.format("0x%02X",compID))
                           .append(", ")
                           .append(String.format("0x%2X",domainID))
                           .append(", ")
                           .append(String.format("0x%2X",channelID))
                           .append(", ")
                           .append(String.format("0x%2X",threadID))
                           .append(", ")
                           .append(level)
                           .append(", ")
                           .append(text)
                           .append("\n");
                    break;
                case 0x01:
                    //Binary
                    byte[] binaryArray = new byte[dataLength];
                    buffer.get(binaryArray, 0, dataLength);
                    String binaryLog = bytesToHex(binaryArray);
                    builder.append(timestamp)
                           .append(", vw-hmi-bin, ")
                           .append(dataLength)
                           .append(", ")
                           .append(bytesToHex(binaryArray))
                           .append("\n");
                    break;
                default: 
                    //Do nothing
                    break;
            }
        }  
        area.setText(builder.toString());
        builder.setLength(0);
        builder.trimToSize();
        channel.close();
    } catch (IOException ex) {
        Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex);
    } catch (Exception ex) {
        Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex);
    }
}

    private final char[] hexArray = "0123456789ABCDEF".toCharArray();
    private String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    public void OutputToFile(String path, String content) throws Exception{
        try (PrintWriter writer = new PrintWriter(path, "UTF-8")) {
            writer.println(content);
            writer.close();
        }
    }

    private String levelToString(byte level) {
        String levelString = null;
        int levelValue = Math.abs(level);
        switch(levelValue) {
            case 0:
                levelString = "All";
                break;
            case 32:
                levelString = "Trace";
                break;
            case 64:
                levelString = "Debug";
                break;
            case 96:
                levelString = "Normal";
                break;
            case 128:
                levelString = "Info";
                break;
            case 160:
                levelString = "Warn";
                break;
            case 192:
                levelString = "Error";
                break;
            case 224:
                levelString = "Fatal";
                break;
        }
        return levelString;
    }
}

最好的方法是启用详细的 GC 日志,然后跟踪触发 full GC 的时间,这基本上涵盖了新生代、幸存者和老一代的 GC(您的字符串构建器对象可能会在任何这些地区)。

所以仍然在 full GC 之后,如果您发现内存没有被释放,那么最好的工具是使用 eclipse MAT 并跟踪哪些对象正在占用内存。

比方说,如果您发现 class X 中的对象 X 拥有最多的内存,那么您可以分析该对象或 class。 Eclipse MAT 还会为您提供查看对象保存的数据的选项。

所以我建议您不要通过您正在跟踪的这个堆大小来检查。但是启用详细的 GC 日志。分析它们,如果您仍然发现任何问题,则通过 JvisualVM、eclipse MAT、Java Mission Controller 等分析工具进行分析

Google了解详细的 GC 日志以及如何启用它们