¿JDK ClassLoader.getResourceAsStream está roto? (recursos no cerrados)

Trataré de demostrar queClassLoader.getResourceAsStream() está abriendo dosInputStreams, cerrando ninguno de ellos y devolviendo solo uno al cliente. ¿Es correcta mi lógica? Las fuentes JDK se seleccionan de jdk1.8.0_25

Me metí en un problema de recursos no cerrados usando Spring ClassPathResource en intervalos (pregunta original), que está utilizandoClassLoader.getResourceAsStream LlegarInputStream a un archivo de propiedades.

Después de la investigación, descubrí queclassLoader.getResourceAsStream está recibiendo unURL porURL url = getResource(name); y luego está abriendo esa corriente, peroURL url = getResource(name) ya abre esa corriente. Fuente JDK deClassLoader:

    public InputStream getResourceAsStream(String name) {
        URL url = getResource(name); /* SILENTLY OPENS AND DON'T CLOSES STREAM */
        try {
            return url != null ? url.openStream() : null; /* SECOND OPEN !!! */
        } catch (IOException e) {
            return null;
        }
    }

Si lo haremosclose() elInputStream siempre que cerremos solo la secuencia abierta porurl.openStream(). Fuente JDK:

    public final InputStream openStream() throws java.io.IOException {
        return openConnection().getInputStream();
    }

Supongo queEl problema es que el JDK abre una secuencia silenciosamente enURL url = getResource(name) solo para obtener el objeto URL que se usa más para crear ** segundo (devuelto al cliente) flujo **. Mira las fuentes de este método:

    public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name); <---- we end up calling that method
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }

Y ahora, engetBootstrapResource(name) el momento cuando convertimosResource aURL olvidando la corriente abierta enResource!:

private static URL getBootstrapResource(String name) {
    URLClassPath ucp = getBootstrapClassPath();
    Resource res = ucp.getResource(name); <---- OPENING STREAM [see further]
    return res != null ? res.getURL() : null; <--- LOSING close() CAPABILITY
}

Por quéucp.getResource(name); está abriendo recurso? Veamos ese método:this.getResource(var1, true);, que delega a:

public Resource getResource(String var1, boolean var2) {
    if(DEBUG) {
        System.err.println("URLClassPath.getResource(\"" + var1 + "\")");
    }

    URLClassPath.Loader var3;
    for(int var4 = 0; (var3 = this.getLoader(var4)) != null; ++var4) {
        Resource var5 = var3.getResource(var1, var2); <-------- OPENING STREAM
        if(var5 != null) {
            return var5;
        }
    }

    return null;
}

Por quéResource var5 = var3.getResource(var1, var2); está abriendo stream? Mira más allá:

Resource getResource(final String var1, boolean var2) {
        final URL var3;
        try {
            var3 = new URL(this.base, ParseUtil.encodePath(var1, false));
        } catch (MalformedURLException var7) {
            throw new IllegalArgumentException("name");
        }

        final URLConnection var4;
        try {
            if(var2) {
                URLClassPath.check(var3);
            }

            var4 = var3.openConnection(); <------------ OPENING STREAM
            InputStream var5 = var4.getInputStream();
            if(var4 instanceof JarURLConnection) {
                JarURLConnection var6 = (JarURLConnection)var4;
                this.jarfile = URLClassPath.JarLoader.checkJar(var6.getJarFile());
            }
        } catch (Exception var8) {
            return null;
        }

        return new Resource() {
            public String getName() {
                return var1;
            }

            public URL getURL() {
                return var3;
            }

            public URL getCodeSourceURL() {
                return Loader.this.base;
            }

            public InputStream getInputStream() throws IOException {
                return var4.getInputStream();
            }

            public int getContentLength() throws IOException {
                return var4.getContentLength();
            }
        };
    }

Podemos veropenConnection() ygetInputStream(), que no están cerrados, y retrocediendo a través de todas las llamadas que regresanResource finalmente estamos usando solo elgetURL() método envuelto enResource sin cerrar esInputStream solo para usar esoURL objetar abrir otro jetInputStream y devolverlo a un cliente (qué cliente puede cerrar de coruse, pero terminamos con la primera secuencia sin cerrar).

Entonces,¿ClassLaoder.getResourceAsStream está roto con recursos con fugas?

Lado práctico: estoy usandogetResourceAsStream entry-with-resources bloquean y aún tienen problemas de recursos no cerrados en producción con el nombre de archivo cargado de esta manera cada 30 segundos. Además, todos esos recursos están cerrados en la recolección de basura, lo que es coherente con la secuencia de archivosclose() enfinalize() método.

Respuestas a la pregunta(1)

Su respuesta a la pregunta