java 中持续将小文件读入对象的最有效方法

Most efficient way in java to continually read small files into an object

tl/dr:我需要让我的应用程序中的一些值与 ~10 个小文件中的值保持最新,但我担心一遍又一遍地读取这些值会产生大量 GC 开销.我是创建一堆无缓冲的文件读取器并轮询它们,还是有什么方法可以 "map" 将文件中的值转换为 java Double 以便稍后我可以重新 运行当值(可能)改变时?

长版本:我有一些物理传感器(陀螺仪、转速计),它们 ev3dev 有助于将它们的当前值公开为虚拟文件系统中的小文件。就像一个名为“/sys/bus/lego/drivers/ev3-analog-sensor/angle”的文件包含 56.26712

或者下一刻它包含58.9834

而且我希望我的应用程序中的值尽可能与该文件保持同步。我可以让你的标准循环包含 MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());(来自 here),但如果将其置于快速循环中,这似乎会产生大量分配开销。

也许有扫描仪,或者 FileChannel inChannel = aFile.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); while(inChannel.read(buffer) > 0)...

没找到KeepInSyncWithFile(myFloatArray, File("./angle", MODE.FILE_TO_VALUE, 10, TimeUnits.MS)

的神奇功能

Java8+

我不能保证这一点,但是 File Observer 可能值得研究。您可以在您的应用程序中缓存最新值并通过 FileObserver 观察文件以查找是否发生任何修改事件。我个人没有任何使用它的经验,所以我不能肯定地说它是否适用于系统文件。但如果确实如此,那么与在循环中重复查找文件相比,这是一个更好的解决方案。

你说的是 /sys 虚拟文件系统上的伪文件,标准 WatchService 不太可能适用于它们。为了获得更新的值,您需要阅读这些文件。

好消息是您可以 garbage-free 方式继续阅读,即完全没有分配。打开文件并只分配一次缓冲区,每次要读取一个值时,查找文件的开头并读取到现有的预分配缓冲区。

代码如下:

public class DeviceReader implements Closeable {
    private final RandomAccessFile file;
    private final byte[] buf = new byte[512];

    public DeviceReader(String fileName) throws IOException {
        this.file = new RandomAccessFile(fileName, "r");
    }

    @Override
    public void close() throws IOException {
        file.close();
    }

    public synchronized double readDouble() throws IOException {
        file.seek(0);
        int length = file.read(buf);
        if (length <= 0) {
            throw new EOFException();
        }

        int sign = 1;
        long exp = 0;
        long value = 0;

        for (int i = 0; i < length; i++) {
            byte ch = buf[i];
            if (ch == '-') {
                sign = -1;
            } else if (ch == '.') {
                exp = 1;
            } else if (ch >= '0' && ch <= '9') {
                value = (value * 10) + (ch - '0');
                exp *= 10;
            } else if (ch < ' ') {
                break;
            }
        }

        return (double) (sign * value) / Math.max(1, exp);
    }
}

请注意,我从 byte[] 缓冲区手动解析浮点数。调用 Double.parseDouble 会容易得多,但在这种情况下,您必须将 byte[] 转换为 String,算法将不再是免费分配的。