Jak odmapować Plik z pamięci zmapowanej za pomocą FileChannel w Javie?

Mapuję plik ("sample.txt") do pamięci używając FileChannel.map(), a następnie zamykając kanał używając fc.close(). Po tym, gdy zapisuję do pliku za pomocą FileOutputStream, otrzymuję następujący błąd:

Java. io. FileNotFoundException: próbka.txt (żądana operacja nie może być utworzony na pliku z user-mapped section open)

File f = new File("sample.txt");
RandomAccessFile raf = new RandomAccessFile(f,"rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
fc.close();
raf.close();

FileOutputStream fos = new FileOutputStream(f);
fos.write(str.getBytes());
fos.close();

Przypuszczam, że może to być spowodowane tym, że plik jest nadal mapowany do pamięci nawet po zamknięciu FileChannel. Mam rację?. Jeśli tak, Jak mogę "odmapować" Plik z pamięci?(Nie mogę znaleźć żadnych metod na to w API). Dzięki.

Edytuj: Wygląda na to, że(dodanie metody unmap) został złożony jako RFE do sun jakiś czas temu: http://bugs.sun.com/view_bug.do?bug_id=4724038

Author: learner135, 2010-06-04

11 answers

Z MappedByteBuffer javadoc:

Zmapowany bufor bajtowy i odwzorowanie Pliku, które reprezentuje, pozostają ważne, dopóki sam bufor nie zostanie usunięty.

Spróbuj zadzwonić. Nawet to tylko sugestia dla VM.
 7
Author: scompt.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 09:58:13

Można zastosować następującą metodę statyczną:

public static void unmap(MappedByteBuffer buffer)
{
   sun.misc.Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
   cleaner.clean();
}

Ale jest to niebezpieczne rozwiązanie ze względu na następujące:
1) prowadzić do awarii, jeśli ktoś używa MappedByteBuffer po unmap
2) opiera się na szczegółach implementacji MappedByteBuffer

 36
Author: Timur Yusupov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-24 20:31:47

[WinXP, SunJDK1.6] miałem zmapowany ByteBuffer pobrany z filechannel. Po przeczytaniu tak posty w końcu udało się zadzwonić czyściciel przez odbicie bez żadnego słońca.* import pakietów. Blokada plików już nie trwa.

FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteBuffer cb = null;
try {
    long size = fc.size();
    cb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size);
    ...do the magic...
finally {
    try { fc.close(); } catch (Exception ex) { }
    try { fis.close(); } catch (Exception ex) { }
    closeDirectBuffer(cb);
}

private void closeDirectBuffer(ByteBuffer cb) {
    if (cb==null || !cb.isDirect()) return;

    // we could use this type cast and call functions without reflection code,
    // but static import from sun.* package is risky for non-SUN virtual machine.
    //try { ((sun.nio.ch.DirectBuffer)cb).cleaner().clean(); } catch (Exception ex) { }
    try {
        Method cleaner = cb.getClass().getMethod("cleaner");
        cleaner.setAccessible(true);
        Method clean = Class.forName("sun.misc.Cleaner").getMethod("clean");
        clean.setAccessible(true);
        clean.invoke(cleaner.invoke(cb));
    } catch(Exception ex) { }
    cb = null;
}

Pomysły zostały zaczerpnięte z tych postów.
* Jak odmapować Plik z pamięci zmapowanej za pomocą FileChannel w Javie?
* przykłady wymuszania wyzwalania pamięci natywnej przydzielił bezpośredni ByteBuffer, używając sun.misc.Niebezpieczne?
* https://github.com/elasticsearch/elasticsearch/blob/master/src/main/java/org/apache/lucene/store/bytebuffer/ByteBufferAllocator.java#L40

 17
Author: Whome,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:44

Sun.misc.Cleaner javadoc says:

Środki czyszczące ogólnego przeznaczenia na bazie phantom-reference. Środki czyszczące są lekką i solidniejszą alternatywą dla finalizacji. Są one lekkie, ponieważ nie są tworzone przez maszynę wirtualną i dlatego nie wymagają utworzenia pliku JNI upcall, a ich kod oczyszczania jest wywoływany bezpośrednio przez wątek reference-handler, a nie przez wątek finalizer. Są bardziej wytrzymałe, ponieważ używają phantom reference, najsłabszego typu obiekt odniesienia, unikając w ten sposób nieprzyjemnych problemów związanych z zamawianiem związanych z finalizacją. Czyszczenie śledzi obiekt referencyjny i zawiera odgłos dowolnego kodu czyszczenia. Jakiś czas po tym, jak GC wykryje, że referent czyszczenia stał się phantom-reachable, wątek reference-handler uruchomi cleaner. Środki czyszczące mogą być również wywoływane bezpośrednio; są one bezpieczne i zapewniają, że uruchamiają swoje odgłosy co najwyżej raz. Środki czyszczące nie zastępują finalizacji. Powinny być używany tylko wtedy, gdy kod czyszczenia jest niezwykle prosty i prosty. Nietrywialne środki czyszczące są niewskazane, ponieważ ryzykują zablokowanie wątku reference-handler i opóźnienie dalszego czyszczenia i finalizacji.

Uruchomiony System.gc () jest akceptowalnym rozwiązaniem, jeśli całkowity rozmiar buforów jest mały, ale gdybym mapował gigabajty plików, spróbowałbym zaimplementować tak:

((DirectBuffer) buffer).cleaner().clean()
Ale! Upewnij się, że nie masz dostępu do tego bufora po wyczyszczeniu, bo skończysz z:

W Java Runtime Environment Wykryto błąd krytyczny: EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000002bcf700, pid= 7592, tid=10184 wersja JRE: Java(TM) SE Runtime Environment (8.0_40-b25) (build 1.8.0_40-b25) Java VM: Java HotSpot(TM) 64-Bit Server VM (25.40-B25 mixed mode windows-amd64 compressed oops) Problematyczna ramka: J 85 C2 java.nio.DirectByteBuffer.get (I)B (16 bajtów) @ 0x0000000002bcf700 [0x000000002bcf6c0+0x40] nie udało się napisać zrzut rdzenia. Minidumpy nie są domyślnie włączone w wersjach klienckich Windows plik raportu o błędzie z większą ilością informacji jest zapisywany jako: C:\Users\?????\Programs \ testApp \ hs_err_pid7592.metoda logowania (c2) 42392 85 40nio.DirectByteBuffer:: get (16 bajtów) razem w stercie [0x0000000002bcf590, 0x000000002bcf828] = 664 Relokacja [0x0000000002bcf6b0,0x000000002bcf6c0] = 16 kod główny [0x0000000002bcf6c0,0x000000002bcf760] = 160 kod stub
[0x0000000002bcf760, 0x000000002bcf778] = 24 oops
[0x0000000002bcf778,0x000000002bcf780] = 8 metadanych
[0x0000000002bcf780, 0x000000002bcf798] = 24 dane zakresów
[0x0000000002bcf798, 0x000000002bcf7e0] = 72 lunety szt.
[0x0000000002bcf7e0,0x000000002bcf820] = 64 zależności
[0x0000000002bcf820, 0x000000002bcf828] = 8

Powodzenia!
 5
Author: Dmitrius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-23 16:21:54

Aby obejść ten błąd w Javie, musiałem wykonać następujące czynności, które będą działać ok dla małych i średnich Plików:

    // first open the file for random access
    RandomAccessFile raf = new RandomAccessFile(file, "r");

    // extract a file channel
    FileChannel channel = raf.getChannel();

    // you can memory-map a byte-buffer, but it keeps the file locked
    //ByteBuffer buf =
    //        channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());

    // or, since map locks the file... just read the whole file into memory
    ByteBuffer buf = ByteBuffer.allocate((int)file.length());
    int read = channel.read(buf);

    // .... do something with buf

    channel.force(false);  // doesn't help
    channel.close();       // doesn't help
    channel = null;        // doesn't help
    buf = null;            // doesn't help
    raf.close();           // try to make sure that this thing is closed!!!!!
 2
Author: Mr Ed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-19 09:22:15

Zmapowana pamięć jest używana do czasu jej uwolnienia przez garbage collector.

From FileChannel docs

Mapowanie, po ustaleniu, nie jest zależne od kanału plików, który został użyty do jego utworzenia. Zamknięcie kanału, w szczególności, nie ma wpływu na ważność mapowania.

From MappedByteBuffer java doc

Mapowany bufor bajtowy i odwzorowanie Pliku, które reprezentuje, pozostają ważne, dopóki sam bufor nie zostanie zbierane śmieci.

Sugerowałbym więc upewnienie się, że nie ma pozostałych odniesień do mapowanego bufora bajtów, a następnie zażądanie usunięcia śmieci.

 1
Author: BenM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 09:59:06

Znalazłem informacje o unmap, jest to metoda FileChannelImpl i nie jest dostępna, więc można ją wywołać przez java reflect w stylu:

public static void unMapBuffer(MappedByteBuffer buffer, Class channelClass) {
    if (buffer == null) {
        return;
    }

    try {
        Method unmap = channelClass.getDeclaredMethod("unmap", MappedByteBuffer.class);
        unmap.setAccessible(true);
        unmap.invoke(channelClass, buffer);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}
 1
Author: 饒夢楠,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 14:05:22

To zabawne widzieć tak wiele zaleceń, aby robić to, czego Punkt 7 w "efektywnej Javie" wyraźnie mówi, aby nie robić. Potrzebna jest metoda zakończenia, taka jak to, co zrobił @Whome i brak odniesień do bufora. GC nie może być wymuszony. Ale to nie powstrzymuje deweloperów przed próbami. Innym obejściem, które znalazłem, było użycie słabych referencji z http://jan.baresovi.cz/dr/en/java#memoryMap

final MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size);
....
final WeakReference<mappedbytebuffer> bufferWeakRef = new WeakReference<mappedbytebuffer>(bb);
bb = null;

final long startTime = System.currentTimeMillis();
while(null != bufferWeakRef.get()) {
  if(System.currentTimeMillis() - startTime > 10)
// give up
    return;
    System.gc();
    Thread.yield();
}
 0
Author: Droid Teahouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-23 23:50:35

Ja bym spróbował JNI:

#ifdef _WIN32
UnmapViewOfFile(env->GetDirectBufferAddress(buffer));
#else
munmap(env->GetDirectBufferAddress(buffer), env->GetDirectBufferCapacity(buffer));
#endif

Dołącz pliki: windows.h Dla Windows, sys / mmap.h dla BSD, Linux, OSX.

 0
Author: martins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-12 06:39:05

Jeśli można zagwarantować, że obiekt mapowany bufor plików kwalifikuje się do usuwania śmieci, nie trzeba GC całej maszyny Wirtualnej, aby zwolnić zmapowaną pamięć bufora. Możesz zadzwonić do systemu.runFinalization (). Spowoduje to wywołanie metody finalize () na obiekcie mapowanego bufora plików (jeśli nie ma do niej odniesień w wątkach aplikacji), która zwolni zmapowaną pamięć.

 -2
Author: somedude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 11:44:56

Poprawnym rozwiązaniem jest użycie try-with-resources.

Pozwala to na utworzenie kanału i innych zasobów, które mają być ograniczone do bloku. Po wyjściu bloku, kanał i inne zasoby znikają i nie mogą być użyte (ponieważ nic nie ma do nich odniesienia).

Mapowanie pamięci nadal nie zostanie cofnięte do następnego uruchomienia GC, ale przynajmniej nie ma żadnych zwisających odniesień do niego.

 -12
Author: kittylyst,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-05 14:56:04