Я пытаюсь отследить утечку памяти в приведенном ниже коде Java.
- Этот код выдает
OutOfMemoryError: heap space
при работе с максимальным размером кучи 2 ГБ после обработки только несколько обновлений файлов. - Размер сжатых файлов на S3 не превышает нескольких сотен килобайт.
- Входная строка (переменная "mentment ") не превышает 1 МБ.
- Дамп кучи показывает размер
byte[]
сотен МБ - в одном случае он занимал более 50% пространства кучи. (Однако инструментальные средства не указали, где создается этот массив).
Он использует клиентский экземпляр AmazonS3 для чтения и записи объектов в удаленной корзине S3, а также использует ObjectMapper Джексона для работы с Строки и их преобразование в JSON et c. Классы Deflater и Inflater из пакета java.util.zip
используются для сжатия / распаковки.
Есть ли очевидная утечка памяти в этом коде? Есть ли очевидная ошибка, которая может привести к постоянно растущему / огромному байтовому массиву в куче? Я действительно почесал голову над этим какое-то время! Спасибо.
class FileManager {
// Constructor omitted
private static final int BYTE_SIZE = 1024;
private final String bucketName;
private final AmazonS3 client;
private final ObjectMapper objectMapper;
public void saveFile(String key, String amendment, String updateTimeStamp) throws Exception {
S3Object object = null;
try {
JsonNode amendmentJson = objectMapper.readTree(amendment);
ObjectNode targetJson;
if (client.doesObjectExist(bucketName, key)) {
object = client.getObject(bucketName, key);
InputStream objectStream = object.getObjectContent();
targetFile = (ObjectNode) decompress(objectStream);
objectStream.close();
object.close();
} else {
targetJson = objectMapper.createObjectNode();
}
targetJson.set(updateTimeStamp, amendmentJson);
byte[] contentBytes = compress(objectMapper.writeValueAsBytes(targetJson));
try (ByteArrayInputStream contentStream = new ByteArrayInputStream(contentBytes)) {
client.putObject(bucketName, key, contentStream);
}
} catch (Exception ex) {
// Logs and rethrows ...
}
}
public byte[] compress(byte[] data) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
Deflater deflater = new Deflater();
byte[] buffer = new byte[BYTE_SIZE];
deflater.setInput(data);
deflater.finish();
while (!deflater.finished()) {
int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
deflater.end();
return outputStream.toByteArray();
}
public JsonNode decompress(InputStream objectReader) throws IOException {
byte[] data = IOUtils.toByteArray(objectReader);
byte[] buffer = new byte[BYTE_SIZE];
Inflater inflater = new Inflater();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
String outputString;
inflater.setInput(data);
try {
while (!inflater.finished()) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}
outputString = new String(outputStream.toByteArray());
} catch (DataFormatException e) {
log.info("Content not zipped");
outputString = new String(data);
}
outputStream.close();
inflater.end();
StringReader reader = new StringReader(outputString);
JsonNode jsonNode = objectMapper.readTree(reader);
reader.close();
return jsonNode;
}
}