Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to free all resources after reading a JRT?

I am trying to read the list of modules available in a given Java 9+ installation, given its Java Home, using the method described in How to extract the file jre-9/lib/modules?.

The solution works, but it appears that the resources allocated to read the content of the Java Runtime Image are never freed, causing a memory leak, observable with VisualVM for instance:

Memory leak as observed with VisualVM

How can I fix the memory leak in the following reproduction?

package leak;

import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Stream;

public class JrtfsLeak {
  public static void main(String[] args) throws Exception {
    Path javaHome = Paths.get(args[0]);
    for (int i = 0; i < 100000; ++i) {
      modules(javaHome).close();
    }
  }

  private static Stream<Path> modules(Path javaHome) throws Exception {
    Map<String, String> env = Collections.singletonMap("java.home", javaHome.toString());
    Path jrtfsJar = javaHome.resolve("lib").resolve("jrt-fs.jar");
    try (URLClassLoader classloader = new URLClassLoader(new URL[] { jrtfsJar.toUri().toURL() })) {
      try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env, classloader)) {
        Path modulesRoot = fs.getPath("modules");
        return Files.list(modulesRoot);
      }
    }
  }
}
like image 685
Martin Avatar asked Jun 22 '21 12:06

Martin


People also ask

How do I look after a Jack Russell?

Jack Russells have endless energy and need at least 1 hour of daily exercise, as well as plenty of playtime! True to their terrier nature, Jack Russells are active, curious dogs who love to explore. This means that they need plenty of exercise as well as mental stimulation to keep them happy, despite their small size.

Why does my Jack Russell have fits?

If no other cause can be found, the disease is called primary, or idiopathic epilepsy. This problem is often an inherited condition, with Jack Russell Terriers commonly afflicted. If your friend is prone to seizures, they will usually begin between six months and three years of age.

What problems do Jack Russells have?

Common health issues affecting the Jack Russell breed include inherited eye diseases and deafness. Legg Perthes is a disease of the hip joints that can occur most commonly in smaller breed dogs, the Jack Rusell included. They are also prone to dislocation of the knee caps.

What illness are Jack Russells prone to?

A Jack Russell is more likely to suffer from a liver disorder known as portosystemic shunt (PSS), which deprives the liver of the blood flow it needs to grow and function properly.

How do I read a file from a resource in Java?

Methods in the classes Class and ClassLoader provide a location-independent way to locate resources. We can read a file from the application’s resources package by using ClassLoader reference. The method getResource () returns a URL for the resource.

How do you use readtheory?

ReadTheory: Using ReadTheory, students take a placement pre-test and then are assigned articles based on the pre-test. As the students continue to read passages and answer questions, the articles are assigned based on their performance on the questions. Reports include grade-level and Lexile-level performance.

Where can I find free reading passages for students?

CommonLit: Free reading passages and other literacy resources. Includes literature and non-fiction for 3-12 grades. Track student progress on reading and writing skills. Tween Tribune: View each article at multiple reading levels. Search by grade level or content area.

What are the best online reading resources for students?

Newsela: Allows users to assign leveled reading passages with questions. Paid features include viewing individual progress reports, assigning with instructions, and customizable writing prompts. CommonLit: Free reading passages and other literacy resources.


Video Answer


2 Answers

This is a JDK bug JDK-8260621 that has been fixed in JDK 17.
It was caused by a careless use of thread locals in ImageBufferCache.

like image 89
apangin Avatar answered Oct 22 '22 09:10

apangin


Note that when you are running under Java 9 or newer, the underlying implementation will create a new class loader for the jrt-fs.jar when you specify the java.home option. So, these classes are not loaded by your URLClassLoader but a different class loader. When you don’t need support for versions prior to 9, you can omit the creation of a class loader.

In either case, they’re loaded by a custom class loader and could be unloaded when the garbage collector supports it. But the class jdk.internal.jimage.ImageBufferCache contains:

private static final ThreadLocal<BufferReference[]> CACHE =
    new ThreadLocal<BufferReference[]>() {
        @Override
        protected BufferReference[] initialValue() {
            // 1 extra slot to simplify logic of releaseBuffer()
            return new BufferReference[MAX_CACHED_BUFFERS + 1];
        }
    };

As explained in How does this ThreadLocal prevent the Classloader from getting GCed, a backreference from the value to the thread local can prevent its garbage collection and when the thread local is stored in a static variable, a reference to one of the classes loaded by the same class loader is enough.

And here, the value is an array of BufferReference, which means even when all entries of that array have been cleared, the array type itself has an implicit reference to the class loader of that filesystem.

But since its a thread local variable, we can work-around it by letting the key thread die. When I change your code to

public static void main(String[] args) throws InterruptedException {
    Path javaHome = Paths.get(args[0]);
    Runnable r = () -> test(javaHome);
    for(int i = 0; i < 1000; ++i) {
        Thread thread = new Thread(r);
        thread.start();
        thread.join();
    }
}

static void test(Path javaHome) {
    for (int i = 0; i < 1000; ++i) {
        try(var s = modules(javaHome)) {}
        catch(IOException ex) {
            throw new UncheckedIOException(ex);
        }
        catch(Exception ex) {
            throw new IllegalStateException(ex);
        }
    }
}

the classes get unloaded.

like image 45
Holger Avatar answered Oct 22 '22 08:10

Holger