JDK ClassLoader.getResourceAsStream не работает? (нераскрытые ресурсы)
Я постараюсь доказать этоClassLoader.getResourceAsStream()
открывает дваInputStreams
не закрывая ничего из этого и возвращая только один клиенту. Правильна ли моя логика? Источники JDK взяты из jdk1.8.0_25
Я попал в проблему нераскрытых ресурсов, используя Spring ClassPathResource в интервале (оригинальный вопрос), который используетClassLoader.getResourceAsStream
получитьInputStream
в файл свойств.
После расследования я обнаружил, чтоclassLoader.getResourceAsStream
получаетURL
отURL url = getResource(name);
и тогда он открывает этот поток, ноURL url = getResource(name)
уже открывает этот поток, JDK источникClassLoader
:
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;
}
}
Если мы будемclose()
InputStream
при условии, что мы закроем только поток, открытыйurl.openStream()
, JDK источник:
public final InputStream openStream() throws java.io.IOException {
return openConnection().getInputStream();
}
Я полагаю, чтопроблема в том, что JDK тихо открывает поток вURL url = getResource(name)
только для получения объекта URL, который используется в дальнейшем для создания ** секунд (возвращается клиенту) поток **. Посмотрите на источники этого метода:
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;
}
И теперь, вgetBootstrapResource(name)
момент, когда мы конвертируемResource
вURL
забыв об открытом потоке вResource
!:
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
}
Зачемucp.getResource(name);
такое открывающийся ресурс? Давайте посмотрим на этот метод:this.getResource(var1, true);
который делегирует:
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;
}
ЗачемResource var5 = var3.getResource(var1, var2);
это открытие потока? Смотри дальше:
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();
}
};
}
Мы можем увидетьopenConnection()
а такжеgetInputStream()
, которые не закрыты, и отступают через все звонки, возвращающиесяResource
наконец, мы используем толькоgetURL()
метод, завернутый вResource
не закрывая этоInputStream
только чтобы использовать этоURL
возражать открыть самолет другойInputStream
и вернуть его клиенту (какой клиент может закрыть coruse, но мы заканчиваем с первым закрытым потоком).
Так,не работает ClassLaoder.getResourceAsStream из-за утечки ресурсов?
Практическая сторона: я пользуюсьgetResourceAsStream
вtry-with-resources
блокировать, и все еще имеют нераскрытые проблемы с ресурсами при работе с именем файла, загружаемым таким образом каждые 30 секунд. Более того, все эти ресурсы закрыты на сборку мусора, что согласуется с потоком файловclose()
вfinalize()
метод.