我有一个内存泄漏,我已经隔离到不正确的直接字节缓冲区.
ByteBuffer buff = ByteBuffer.allocateDirect(7777777);
GC收集包含这些缓冲区的对象,但不会丢弃缓冲区本身.如果我实例化足够的包含缓冲区的瞬态对象,我会得到这个令人鼓舞的消息.
java.lang.OutOfMemoryError: Direct buffer memory
我一直在寻找这个问题,显然
buff.clear();
和
System.gc();
不起作用
JDK提供了分配所谓的直接ByteBuffers的能力,其中内存在Java堆之外分配.这可能是有益的,因为垃圾收集器不会触及这个内存,因此不会导致GC开销:这对于像缓存这样的长期存在的东西来说非常有用.
但是,现有实现存在一个关键问题:当拥有ByteBuffer被垃圾收集时,底层内存只是异步分配; 没有办法强迫早期解除分配.这可能有问题,因为GC循环本身不受ByteBuffers处理的影响,并且假设ByteBuffers可能驻留在Old Generation内存区域,则可能在ByteBuffer不再使用后几小时调用GC.
但理论上应该可以直接使用sun.misc.Unsafe方法(freeMemory,allocateMemory):这就是JDK本身用于分配/解除分配本机内存的方法.看一下代码,我看到的一个潜在问题是内存双重释放的可能性 - 所以我想确保正确清理状态.
任何人都可以指向我这样做的代码吗?理想情况下,想要使用它而不是JNA.
注意:我看到这个问题有点相关.
看起来指出的答案是很好的方法:这里是使用这个想法的Elastic Search的代码示例.谢谢大家!
我要求将记录写入文件,其中数据根据数字键的值写入文件位置(即搜索位置).例如,如果键为100,我可能会写入位置400.
记录由数字键和一段数据组成.记录不会很大(几个字节).但是,可能会有很多记录(数百万).
有两种可能的情况:
键是单调增加的.在这种情况下,最好的方法是使用DataOutputStream包装a 进行写入BufferedOutputStream,将缓冲区大小设置为某个数字(例如64k)以最大化I/O吞吐量.
密钥正在增加,但可能存在较大差距.在这种情况下,使用OutputStream将需要在文件的间隙中写入零.为了避免这种情况,RandomAccessFile它可以更好,因为它可以寻找空隙,如果可以寻找整个块,节省空间.缺点是,据我所知,RandomAccessFile不缓冲,因此这种方法对于顺序密钥来说会很慢.
但是,可能的情况是文件是两者兼而有之.有一系列单调增加的密钥.有一些键间隙很小,其他键间隙很大.
我正在寻找的是一个提供两全其美的解决方案.如果检测到键之间的间隙,则可能在两个I/O模式之间切换.但是,如果有一个标准的Java类可以完成这两件事,那就更好了.我见过FileImageOutputStream,但我不确定这是怎么回事.
请注意,我不是在寻找代码示例(虽然这对于演示复杂的解决方案很有帮助),但这只是一个通用策略.最好知道顺序数据的最佳大小缓冲区大小以及从顺序策略切换到随机访问策略需要的时间点(间隙大小).
编辑:
为了接受答案,我希望保证所提出的解决方案能够处理两者,而不仅仅是它可能.这需要:
此外,解决方案需要具有内存效率,因为可能会同时打开许多这些文件.
编辑2
这些文件可以在NAS上.这不是设计,而是简单地认识到在企业环境中,这种架构被大量使用,解决方案可能应该处理它(可能不是最佳的)而不是阻止它的使用.AFAIK,这不应该影响基于write()和的解决方案lseek(),但可能会使一些更深奥的解决方案失效. 
我正在使用内存映射IO作为索引文件,但问题是如果文件大部分为空,我无法调整文件大小.
之前的某个地方:
MappedByteBuffer map = raf.getChannel().map(MapMode.READ_WRITE, 0, 1 << 30);
raf.close();
// use map
map.force();
map = null;
调整:
for (int c = 0; c < 100; c++) {
    RandomAccessFile raf = new RandomAccessFile(indexFile, "rw");
    try {
        raf.setLength(newLen);
        if (c > 0) LOG.warn("used " + c + " iterations to close mapped byte buffer");
        return;
    } catch (Exception e) {
        System.gc();
        Thread.sleep(10);
        System.runFinalization();
        Thread.sleep(10);
    } finally {
        raf.close();
    }
}
当使用Windows或Linux 32位时,我经常遇到解映问题,但在64位Linux生产环境中,一切似乎都没有警告,但文件保持原始大小.
任何人都可以解释为什么会发生这种情况和/或如何解决问题?
我有将某些特定的大型(约15k条目)二进制序列化文件存档提取到磁盘上文件夹的代码。
public void extractExact(Path absolutePath, DoubleConsumer progressConsumer) throws IOException
{
    ...
    // Extract to file channel
    try (final FileOutputStream fos = new FileOutputStream(absolutePath.toFile()))
    {
        PakExtractor.Extract(pakFile, Entry, fos.getChannel(), progressConsumer);
    }
 }
extractExact 函数调用存档中的每个条目。
在这之后,如果我尝试调用Files.delete(<archive_file_path>)方法-我将得到一个异常:  
java.nio.file.FileSystemException:该进程无法访问该文件,因为该文件正在被另一个进程使用。
我在Process Explorer搜索中检查了存档文件,并说我的java.exe可以打开约1.5万个文件(与存档中的文件一样多)
这仅在Windows(jdk1.8.0_162)中发生。在Linux上,“ zombie”打开的文件没有任何问题。
考虑应用程序,它创建5-6个线程,循环中的每个线程为5mb页面大小分配MappedByteBuffer.
MappedByteBuffer b = ch.map(FileChannel.MapMode.READ_ONLY, r, 1024*1024*5);
迟早,当应用程序使用大文件时,会抛出oom
java.io.IOException: Map failed  at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:758)
Caused by: java.lang.OutOfMemoryError: Map failed
        at sun.nio.ch.FileChannelImpl.map0(Native Method)
        at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:755)
根据规范,MappedBuffer应该在GC本身时立即配置直接内存.看起来问题是,MappedBuffer-s的GC编辑太晚了,后来直接内存完成了.
如何避免这种情况?可能会说MappedBuffer隐式处理或使用某种MappedBuffer池
我正在创建一个记事本类型的程序。这是我从文件读取和写入的方法:
BufferedWriter out = null;
try {
    System.out.println("Saving to " + saveFile.getName());
    out = new BufferedWriter(new FileWriter(saveFile));
    out.write(box.getText());
} catch (IOException ex) {
    Logger.getLogger(INoteView.class.getName()).log(Level.SEVERE, null, ex);
    setStatus("ERROR o1");
} finally {
    try {
        out.close();
    } catch (IOException ex) {
        Logger.getLogger(INoteView.class.getName()).log(Level.SEVERE, null, ex);
        setStatus("ERROR o2");
    }
}
和
private static String readFile(File f) throws IOException {
    FileInputStream stream = new FileInputStream(f);
    try {
        FileChannel fc = stream.getChannel();
        MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
        /* Instead of using default, pass in a …编写Java程序中的以下函数是为了读取文件,然后覆盖该文件。
public static void readOverWrite(File dir) throws IOException {
    for (File f : dir.listFiles()) {
        String[] data = readFile(f).split("\n");
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
            for (int i = 0; i < data.length; i++) {
                writer.write((data[i]+"\n"));
            }
            writer.close();
        }
    }
} 
关于尝试运行程序的错误消息是:
Exception in thread "main" java.io.FileNotFoundException: ..\..\data\AQtxt\APW19980807.0261.tml (The requested operation cannot be performed on a file with a user-mapped section open)
    at java.io.FileOutputStream.open(Native Method)
    at java.io.FileOutputStream.<init>(Unknown Source)
    at java.io.FileOutputStream.<init>(Unknown Source)
    at java.io.FileWriter.<init>(Unknown Source)
    at General.SplitCreationDate.splitLine(SplitCreationDate.java:37)
    at General.SplitCreationDate.main(SplitCreationDate.java:53) …我正在处理 java.nio.file.AccessDeniedException 问题。
我有一个 Scala 程序,如果我这样做:
java.nio.file.Files.delete(FileSystems.getDefault().getPath("""D:\Users\Eric\Google Drive (New)\Music\Downloaded\Foreigner [Discography HQ]\1977 - Foreigner\03 - Starrider.mp3""")) 
一切正常。我有一些代码
def delete(path : Path) {
  try {
    println("deleting " + path)
    java.nio.file.Files.delete(path)
  } catch {
    case exception: Exception => System.err.println(exception)
  }
}
val google1 = FileSystems.getDefault().getPath("""D:\Users\Eric\Google Drive\Music\Downloaded\Foreigner [Discography HQ]""")
val google2 = FileSystems.getDefault().getPath("""D:\Users\Eric\Google Drive (New)\Music\Downloaded\Foreigner [Discography HQ]""")
val duplicates = TraversablePaths(List(google1, google2)).duplicateFilesList
println("deleting duplicate files")
duplicates.foreach(_.filter(!_.startsWith(google1)).foreach(delete))
但是当我尝试删除同一个文件时,我得到
java.nio.file.AccessDeniedException: D:\Users\Eric\Google Drive (New)\Music\Downloaded\Foreigner [Discography HQ]\1977 - Foreigner\03 - Starrider.mp3
我能说的最好的是 JVM 要么锁定了文件,要么锁定了文件所在的目录,但我不知道在哪里。检查文件是否相同的代码看起来像
def identical(file1 : Path, …java ×9
bytebuffer ×2
file-io ×2
io ×2
buffer ×1
filechannel ×1
filelock ×1
java-8 ×1
jvm ×1
linux ×1
memory ×1
memory-leaks ×1
nio ×1
overwrite ×1
scala ×1
text-files ×1
windows ×1