mirror of https://github.com/apache/poi.git
Add MemoryVerifier to enable memory leak checking in unit-tests and add
an initial test which verifies some things for XSSF git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1871588 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3fb5e79490
commit
57e5e87e58
|
@ -0,0 +1,144 @@
|
|||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.xssf;
|
||||
|
||||
import org.apache.poi.util.MemoryLeakVerifier;
|
||||
import org.apache.poi.xssf.usermodel.XSSFCell;
|
||||
import org.apache.poi.xssf.usermodel.XSSFRow;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* A test which uses {@link MemoryLeakVerifier} to ensure that certain
|
||||
* objects are not left over in memory after the test.
|
||||
*
|
||||
* E.g. verifies that objects are freed when stuff is removed from sheets or rows
|
||||
*/
|
||||
public class XSSFMemoryLeakTests {
|
||||
private final MemoryLeakVerifier verifier = new MemoryLeakVerifier();
|
||||
|
||||
// keep some items in memory, so checks in tearDown() actually
|
||||
// verify that they do not keep certain objects in memory,
|
||||
// e.g. nested CT... objects which should be released
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
private List<Object> references = new ArrayList<>();
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
verifier.assertGarbageCollected();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteRow() throws IOException {
|
||||
final XSSFWorkbook wb = new XSSFWorkbook();
|
||||
final XSSFSheet sheet1 = wb.createSheet("Sheet1");
|
||||
final XSSFRow row = sheet1.createRow(0);
|
||||
final XSSFCell cell = row.createCell(0);
|
||||
cell.setCellValue("hello");
|
||||
|
||||
// Cannot check the CTCell here as it is reused now and thus
|
||||
// not freed until we free up the Cell itself
|
||||
//verifier.addObject(ctCell);
|
||||
|
||||
try (OutputStream out = new ByteArrayOutputStream(8192)) {
|
||||
wb.write(out);
|
||||
}
|
||||
|
||||
CTCell ctCell = cell.getCTCell();
|
||||
assertSame("The CTCell should not be replaced",
|
||||
cell.getCTCell(), ctCell);
|
||||
assertSame("The CTCell in the row should not be replaced",
|
||||
row.getCTRow().getCArray(0), ctCell);
|
||||
|
||||
wb.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveCellFromRow() throws IOException {
|
||||
final XSSFWorkbook wb = new XSSFWorkbook();
|
||||
final XSSFSheet sheet1 = wb.createSheet("Sheet1");
|
||||
final XSSFRow rowToCheck = sheet1.createRow(0);
|
||||
references.add(rowToCheck);
|
||||
|
||||
XSSFCell cell = rowToCheck.createCell(0);
|
||||
cell.setCellValue("hello");
|
||||
|
||||
// previously the CTCell was still referenced in the CTRow, verify that it is freed
|
||||
verifier.addObject(cell);
|
||||
verifier.addObject(cell.getCTCell());
|
||||
|
||||
rowToCheck.removeCell(cell);
|
||||
|
||||
wb.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove2CellsFromRow() throws IOException {
|
||||
final XSSFWorkbook wb = new XSSFWorkbook();
|
||||
final XSSFSheet sheet1 = wb.createSheet("Sheet1");
|
||||
final XSSFRow rowToCheck = sheet1.createRow(0);
|
||||
references.add(rowToCheck);
|
||||
|
||||
XSSFCell cell1 = rowToCheck.createCell(0);
|
||||
cell1.setCellValue("hello");
|
||||
XSSFCell cell2 = rowToCheck.createCell(1);
|
||||
cell2.setCellValue("world");
|
||||
|
||||
// previously the CTCell was still referenced in the CTRow, verify that it is freed
|
||||
verifier.addObject(cell1);
|
||||
verifier.addObject(cell1.getCTCell());
|
||||
verifier.addObject(cell2);
|
||||
verifier.addObject(cell2.getCTCell());
|
||||
|
||||
rowToCheck.removeCell(cell2);
|
||||
rowToCheck.removeCell(cell1);
|
||||
|
||||
wb.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveRowFromSheet() throws IOException {
|
||||
final XSSFWorkbook wb1 = new XSSFWorkbook();
|
||||
final XSSFSheet sheetToCheck = wb1.createSheet("Sheet1");
|
||||
references.add(sheetToCheck);
|
||||
final XSSFRow row = sheetToCheck.createRow(0);
|
||||
final XSSFCell cell = row.createCell(0);
|
||||
cell.setCellValue(1);
|
||||
|
||||
// ensure that the row-data is not kept somewhere in another member
|
||||
verifier.addObject(row.getCTRow());
|
||||
verifier.addObject(row);
|
||||
verifier.addObject(cell.getCTCell());
|
||||
verifier.addObject(cell);
|
||||
|
||||
sheetToCheck.removeRow(row);
|
||||
|
||||
wb1.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.util;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* A simple utility class that can verify that objects have been successfully garbage collected.
|
||||
*
|
||||
* Usage is something like
|
||||
*
|
||||
* private final MemoryLeakVerifier verifier = new MemoryLeakVerifier();
|
||||
|
||||
{@literal}After
|
||||
public void tearDown() {
|
||||
verifier.assertGarbageCollected();
|
||||
}
|
||||
|
||||
{@literal}Test
|
||||
public void someTest() {
|
||||
...
|
||||
verifier.addObject(object);
|
||||
}
|
||||
|
||||
*
|
||||
* This will verify at the end of the test if the object is actually removed by the
|
||||
* garbage collector or if it lingers in memory for some reason.
|
||||
*
|
||||
* Idea taken from http://stackoverflow.com/a/7410460/411846
|
||||
*/
|
||||
public class MemoryLeakVerifier {
|
||||
private static final int MAX_GC_ITERATIONS = 50;
|
||||
private static final int GC_SLEEP_TIME = 100;
|
||||
|
||||
private final List<WeakReference<Object>> references = new ArrayList<>();
|
||||
|
||||
public MemoryLeakVerifier() {
|
||||
}
|
||||
|
||||
public void addObject(Object object) {
|
||||
references.add(new WeakReference<>(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to perform a full garbage collection so that all weak references will be removed. Usually only
|
||||
* a single GC is required, but there have been situations where some unused memory is not cleared up on the
|
||||
* first pass. This method performs a full garbage collection and then validates that the weak reference
|
||||
* now has been cleared. If it hasn't then the thread will sleep for 100 milliseconds and then retry up to
|
||||
* 50 more times. If after this the object still has not been collected then the assertion will fail.
|
||||
*
|
||||
* Based upon the method described in: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
|
||||
*/
|
||||
public void assertGarbageCollected() {
|
||||
assertGarbageCollected(MAX_GC_ITERATIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only for testing the class itself where we would like to fail faster than 5 seconds
|
||||
* @param maxIterations The number of times a GC will be invoked until a possible memory leak is reported
|
||||
*/
|
||||
void assertGarbageCollected(int maxIterations) {
|
||||
try {
|
||||
for(WeakReference<Object> ref : references) {
|
||||
assertGarbageCollected(ref, maxIterations);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// just ensure that we quickly return when the thread is interrupted
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertGarbageCollected(WeakReference<Object> ref, int maxIterations) throws InterruptedException {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
for (int i = 0; i < maxIterations; i++) {
|
||||
runtime.runFinalization();
|
||||
runtime.gc();
|
||||
if (ref.get() == null)
|
||||
break;
|
||||
|
||||
// Pause for a while and then go back around the loop to try again...
|
||||
//EventQueue.invokeAndWait(Procedure.NoOp); // Wait for the AWT event queue to have completed processing
|
||||
Thread.sleep(GC_SLEEP_TIME);
|
||||
}
|
||||
|
||||
assertNull("Object should not exist after " + MAX_GC_ITERATIONS + " collections, but still had: " + ref.get(),
|
||||
ref.get());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue