gpt4 book ai didi

java - 匿名文件流重用描述符

转载 作者:行者123 更新时间:2023-11-30 03:04:32 25 4
gpt4 key购买 nike

以下代码块(每个都是等效的)会导致意外错误吗?我可以依赖这种行为吗?或者它会发生变化吗?

                     // 1st
FileOutputStream f = new FileOutputStream(...);
// some io, not shown

// 2nd
f = new FileOutputStream(f.getFD());
// some io, not shown

// 3rd
f = new FileOutputStream(f.getFD());
// some io, not shown


static FileOutputStream ExampleFunction(FileOutputStream fos) {
return new FileOutputStream(fos.getFD());
}
// |-- 3rd ------| |-- 2nd ------| |-- 1st ----------------|
ExampleFunction(ExampleFunction(ExampleFunction(new FileOutputStream(...))))

有两种可能的结果,我之前概述过。我总是假设最坏的可能结果,即一旦没有引用持有未引用的对象,它们就会被收集。以下内容与第一个代码块相关。

案例#1:

当第二个FileOutputStream被分配给f时,第一个输出流将不再有任何引用,从而被收集。当它完成时,底层文件描述符(在所有三个流之间共享)将被关闭。此时,第二个 FileOutputStream 上的任何 IO 操作(未显示)都将抛出 IOException。但是,第二个 FileOutputStream 保留对 FileDescriptor(现已关闭)的引用,以便第三个分配的 RHS 上的最终 f.getFD() 确实成功。当第三个 FileOutputStream 分配给 f 时,将收集第二个输出流,并且底层 FileDescriptor 将再次关闭(我相信会生成 IOException)。然而,第三个流上的任何 IO 都会再次失败。

案例#2:

或者,FileDescriptor 保留对已分配给它的所有可关闭对象的强引用。当第二个 FileOutputStream 分配给 f 时,FileDescriptor 维护对第一个 FileOutputStream 的引用,以便它永远不会被收集和最终确定,并且 FileDescriptor 保持打开状态。当第三个 FileOutputStream 分配给 f 时,所有三个流都被描述符引用,并且不符合收集条件。

测试用例:

我没有用于测试的 JDK7,但显然情况 #1 适用( JDK7 FileDescriptor.java ),除非未知的第三方保存引用或垃圾收集器做出特定的豁免。

但是,JDK8 显然更改了 FileDescriptor 以保存可关闭列表,因此案例 #2 适用( JDK8 FileDescriptor.java )。我可以使用以下测试程序确认此行为(在 openjdk8 上):

import java.io.*;
import java.lang.ref.WeakReference;
import java.lang.Thread;
import java.lang.Runtime;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import com.sun.management.UnixOperatingSystemMXBean;
import java.util.ArrayList;
import java.util.Arrays;


class TestThread extends Thread {
static void gc() {
System.gc();

try {
sleep(1000);
}
catch (InterruptedException e) {
System.err.println(e.getMessage());
}
}

static void test(String message,
long fd_count_a,
ArrayList<WeakReference<FileOutputStream>> fw,
OperatingSystemMXBean os,
FileDescriptor fd
) throws IOException {
long fd_count_b = fd_count_b = ((UnixOperatingSystemMXBean) os).getOpenFileDescriptorCount() - fd_count_a;

System.out.println("Results, " + message + ":");
for (int i=0; i<fw.size(); i++) {
String prefix = "fw_" + String.valueOf(i);
if (fw.get(i).get() == null) {
System.out.println(prefix + ":\t\t" + "null");
System.out.println(prefix + " open" + ":\t" + "no");
} else {
System.out.println(prefix + ":\t\t" + fw.get(i).get().toString());
System.out.println(prefix + " open" + ":\t" + (fw.get(i).get().getFD().valid() ? "yes" : "no"));
}
}
System.out.println("fd :\t\t" + ((fd == null) ? "null" : fd.toString()));
System.out.println("fds :\t\t" + String.valueOf(fd_count_b));
System.out.println();
}

public void run() {
try {
run_contents();
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}

public void run_contents() throws IOException {
FileOutputStream f = null;
WeakReference<FileOutputStream> fw_1 = null;
WeakReference<FileOutputStream> fw_2 = null;
WeakReference<FileOutputStream> fw_3 = null;
FileDescriptor fd = null;

OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();

long fd_count_a = fd_count_a = ((UnixOperatingSystemMXBean) os).getOpenFileDescriptorCount();

f = new FileOutputStream("/dev/null");
fw_1 = new WeakReference<FileOutputStream>(f);
f.write(1);
gc();
test("after fw_1", fd_count_a, new ArrayList<WeakReference<FileOutputStream>>(Arrays.asList(fw_1)), os, f.getFD());

f = new FileOutputStream(f.getFD());
fw_2 = new WeakReference<FileOutputStream>(f);
f.write(2);
gc();
test("after fw_2", fd_count_a, new ArrayList<WeakReference<FileOutputStream>>(Arrays.asList(fw_1, fw_2)), os, f.getFD());

f = new FileOutputStream(f.getFD());
fw_3 = new WeakReference<FileOutputStream>(f);
f.write(3);
gc();
test("after fw_3", fd_count_a, new ArrayList<WeakReference<FileOutputStream>>(Arrays.asList(fw_1, fw_2, fw_3)), os, f.getFD());

f.close();

gc();
test("after closing stream", fd_count_a, new ArrayList<WeakReference<FileOutputStream>>(Arrays.asList(fw_1, fw_2, fw_3)), os, f.getFD());

fd = f.getFD();

f = null;

gc();
test("after dereferencing stream", fd_count_a, new ArrayList<WeakReference<FileOutputStream>>(Arrays.asList(fw_1, fw_2, fw_3)), os, fd);

fd = null;

gc();
test("after dereferencing descriptor", fd_count_a, new ArrayList<WeakReference<FileOutputStream>>(Arrays.asList(fw_1, fw_2, fw_3)), os, fd);
}
}

class Test {
public static void main(String[] args) {
TestThread t = new TestThread();
t.start();

try {
t.join();
}
catch (InterruptedException e) {
System.err.println(e.getMessage());
}
}
}

其输出如下:

Results, after fw_1:
fw_0: java.io.FileOutputStream@7afd6488
fw_0 open: yes
fd : java.io.FileDescriptor@743a95a7
fds : 1

Results, after fw_2:
fw_0: java.io.FileOutputStream@7afd6488
fw_0 open: yes
fw_1: java.io.FileOutputStream@70050ff8
fw_1 open: yes
fd : java.io.FileDescriptor@743a95a7
fds : 1

Results, after fw_3:
fw_0: java.io.FileOutputStream@7afd6488
fw_0 open: yes
fw_1: java.io.FileOutputStream@70050ff8
fw_1 open: yes
fw_2: java.io.FileOutputStream@35079f9c
fw_2 open: yes
fd : java.io.FileDescriptor@743a95a7
fds : 1

Results, after closing stream:
fw_0: java.io.FileOutputStream@7afd6488
fw_0 open: no
fw_1: java.io.FileOutputStream@70050ff8
fw_1 open: no
fw_2: java.io.FileOutputStream@35079f9c
fw_2 open: no
fd : java.io.FileDescriptor@743a95a7
fds : 0

Results, after dereferencing stream:
fw_0: java.io.FileOutputStream@7afd6488
fw_0 open: no
fw_1: java.io.FileOutputStream@70050ff8
fw_1 open: no
fw_2: java.io.FileOutputStream@35079f9c
fw_2 open: no
fd : java.io.FileDescriptor@743a95a7
fds : 0

Results, after dereferencing descriptor:
fw_0: null
fw_0 open: no
fw_1: null
fw_1 open: no
fw_2: null
fw_2 open: no
fd : null
fds : 0

然而,根据this bug report,似乎有一种插入力。 - 后来被恢复并推迟 - 以防止 FileDescriptor 保留对可关闭对象的强引用。

所以,我的问题:

  • 我对 JDK7 的假设是否正确 - 它的行为与 JDK8 不同?
  • 我可以依赖 JDK8 的 FileDescriptor 的行为来保存对可关闭对象的强引用吗?或者这会在 JDK 的 future 版本中恢复吗?

编辑:我已发布后续问题 Invalidate Stream without Closing .

最佳答案

Can I depend on the behaviour

您只能依赖于包、类、公共(public)/ protected 字段或公共(public)/ protected 方法 javadoc 或 JLS 中指定的内容。毕竟 Java 实现不需要使用 OpenJDK 的类,它们可以从头开始重新实现接口(interface)。

当某些措辞不清楚时,查看实现可能会很有用,但引用实现不是规范的一部分。

您可以依赖特定于实现的行为,但在这种情况下,应该通过适当的检查来保护它,在可能的情况下提供后备代码路径并进行明显的记录。

据我了解,由于此类问题,不鼓励直接使用 FileDescriptors。例如。 this question显示当拥有 FD 的第一个流关闭时,android 确实关闭了 FD

关于java - 匿名文件流重用描述符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35136421/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com