Skip to content

Commit

Permalink
Issue #371: Repeated creation of type systems can exhaust JVM metaspace
Browse files Browse the repository at this point in the history
- Added test case to check for the leak
  • Loading branch information
reckart committed Aug 15, 2024
1 parent 61dfca3 commit e999f35
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ api-change-report
issuesFixed
.idea
*.iml
.tycho-consumer-pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,42 @@
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;

import java.util.Iterator;
import java.lang.management.ManagementFactory;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.CASRuntimeException;
import org.apache.uima.jcas.JCas;
import org.apache.uima.jcas.JCasRegistry;
import org.apache.uima.jcas.tcas.Annotation;
import org.apache.uima.resource.impl.ResourceManager_impl;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import x.y.z.Sentence;

public class TypeSystemImplTest {
class TypeSystemImplTest {
private TypeSystemDescription tsd;

@BeforeEach
public void setup() {
void setup() {
tsd = getResourceSpecifierFactory().createTypeSystemDescription();
}

@Test
public void thatTypeUsedInJavaAndDeclaredInTypeSytemDoesNotThrowException() throws Exception {
void thatTypeUsedInJavaAndDeclaredInTypeSytemDoesNotThrowException() throws Exception {
tsd.addType(Sentence._TypeName, "", CAS.TYPE_NAME_ANNOTATION);

JCas localJcas = createCas(tsd, null, null).getJCas();
var localJcas = createCas(tsd, null, null).getJCas();
localJcas.setDocumentText("This is a test.");

assertThatNoException() //
.isThrownBy(() -> localJcas.getCasType(Sentence.type));
}

@Test
public void thatTypeUsedInJavaButNotDeclaredInTypeSytemThrowsException() throws Exception {
JCas localJcas = createCas(tsd, null, null).getJCas();
void thatTypeUsedInJavaButNotDeclaredInTypeSytemThrowsException() throws Exception {
var localJcas = createCas(tsd, null, null).getJCas();
localJcas.setDocumentText("This is a test.");

assertThat(JCasRegistry.getClassForIndex(Sentence.type)).isSameAs(Sentence.class);
Expand All @@ -74,8 +75,8 @@ public void thatTypeUsedInJavaButNotDeclaredInTypeSytemThrowsException() throws
}

@Test
public void thatTypeNotInTypeSystemAndWithoutJCasClassThrowsException() throws Exception {
JCas localJcas = createCas(tsd, null, null).getJCas();
void thatTypeNotInTypeSystemAndWithoutJCasClassThrowsException() throws Exception {
var localJcas = createCas(tsd, null, null).getJCas();
localJcas.setDocumentText("This is a test.");

assertThatExceptionOfType(CASRuntimeException.class) //
Expand All @@ -87,13 +88,38 @@ public void thatTypeNotInTypeSystemAndWithoutJCasClassThrowsException() throws E
sanityCheckForCasConsistencyUIMA_738(localJcas);
}

private void sanityCheckForCasConsistencyUIMA_738(JCas localJcas) {
void sanityCheckForCasConsistencyUIMA_738(JCas localJcas) {
// check that this does not leave JCAS in an inconsistent state
// (a check for bug UIMA-738)
Iterator<Annotation> iter = localJcas.getAnnotationIndex().iterator();
var iter = localJcas.getAnnotationIndex().iterator();

assertThat(iter).hasNext();
assertThat(iter.next()) //
.extracting(Annotation::getCoveredText) //
.isEqualTo("This is a test.");
}

@Test
void testMetaspaceExhaustion() throws Exception {
var threshold = 2_500;

var classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
var classesLoadedAtStart = classLoadingMXBean.getLoadedClassCount();

var type = tsd.addType(Sentence._TypeName, "", CAS.TYPE_NAME_ANNOTATION);
type.addFeature(Sentence._FeatName_sentenceLength, null, CAS.TYPE_NAME_INTEGER);

for (var i = 0; i < threshold * 2; i++) {
var resMgr = new ResourceManager_impl();
resMgr.setExtensionClassPath(".", false);
createCas(tsd, null, null, null, resMgr).getJCas();
System.runFinalization();
// Make sure the consolidated type system is evicted from the weak hashmap cache
System.gc();

assertThat(classLoadingMXBean.getLoadedClassCount()) //
.as("High number of new loaded classes during test indicates leak")
.isLessThan(classesLoadedAtStart + threshold);
}
}
}

0 comments on commit e999f35

Please sign in to comment.