Force unmapping MappedByteBuffer
Otherwise, on Windows daemon might hold mapped regions for some time (until those objects are collection) and during the time those file become locked (it's impossible to modify or remove them) Reflection/Unsafe of course is not a cool thing to use, but JDK still (already for 18 years) doesn't have public API for this See https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4724038 And https://stackoverflow.com/questions/2972986/how-to-unmap-a-file-from-memory-mapped-using-filechannel-in-java
This commit is contained in:
committed by
TeamCityServer
parent
4e11c670c6
commit
03e5dc6117
@@ -14,6 +14,7 @@ import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.MappedByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
@@ -26,8 +27,12 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandl
|
||||
init {
|
||||
RandomAccessFile(file, "r").use { randomAccessFile ->
|
||||
val mappedByteBuffer = randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length())
|
||||
ourEntryMap = mappedByteBuffer.parseCentralDirectory().associateBy { it.relativePath }
|
||||
cachedManifest = ourEntryMap[MANIFEST_PATH]?.let(mappedByteBuffer::contentsToByteArray)
|
||||
try {
|
||||
ourEntryMap = mappedByteBuffer.parseCentralDirectory().associateBy { it.relativePath }
|
||||
cachedManifest = ourEntryMap[MANIFEST_PATH]?.let(mappedByteBuffer::contentsToByteArray)
|
||||
} finally {
|
||||
mappedByteBuffer.unmapBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
val entries: MutableMap<EntryInfo, FastJarVirtualFile> = HashMap()
|
||||
@@ -167,9 +172,48 @@ private val cachedOpenFileHandles: FileAccessorCache<File, RandomAccessFileAndBu
|
||||
@Throws(IOException::class)
|
||||
override fun disposeAccessor(fileAccessor: RandomAccessFileAndBuffer) {
|
||||
fileAccessor.first.close()
|
||||
fileAccessor.second.unmapBuffer()
|
||||
}
|
||||
|
||||
override fun isEqual(val1: File, val2: File): Boolean {
|
||||
return val1 == val2 // reference equality to handle different jars for different ZipHandlers on the same path
|
||||
}
|
||||
}
|
||||
|
||||
private val IS_PRIOR_9_JRE = System.getProperty("java.specification.version", "").startsWith("1.")
|
||||
|
||||
private fun ByteBuffer.unmapBuffer() {
|
||||
if (!isDirect) return
|
||||
|
||||
try {
|
||||
if (IS_PRIOR_9_JRE) {
|
||||
val cleaner = this::class.java.getMethod("cleaner")
|
||||
|
||||
cleaner.isAccessible = true
|
||||
|
||||
val clean = Class.forName("sun.misc.Cleaner").getMethod("clean")
|
||||
clean.isAccessible = true
|
||||
|
||||
clean.invoke(cleaner.invoke(this))
|
||||
} else {
|
||||
val unsafeClass = try {
|
||||
Class.forName("sun.misc.Unsafe")
|
||||
} catch (ex: Exception) {
|
||||
// jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,
|
||||
// but that method should be added if sun.misc.Unsafe is removed.
|
||||
Class.forName("jdk.internal.misc.Unsafe")
|
||||
}
|
||||
|
||||
val clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer::class.java)
|
||||
clean.isAccessible = true
|
||||
|
||||
val theUnsafeField = unsafeClass.getDeclaredField("theUnsafe")
|
||||
theUnsafeField.isAccessible = true
|
||||
|
||||
val theUnsafe = theUnsafeField.get(null)
|
||||
|
||||
clean.invoke(theUnsafe, this)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user