mirror of
https://github.com/LamGC/ScalaBot.git
synced 2025-04-29 22:27:31 +00:00
perf(utils): 优化自动释放钩子的资源引用.
原本自动释放钩子对资源的引用, 可能会出现资源已经被关闭, 但仍然无法被 GC 回收的问题. 此次改动, 将会让钩子在关闭资源后, 将资源从列表中移除. 虽然, 自动释放钩子设计上仅会被 System.exit 动作触发, 但保险起见还是加上这个改动.
This commit is contained in:
parent
830f05c90a
commit
478480014a
@ -81,17 +81,20 @@ private object UtilsInternal {
|
|||||||
val autoCloseableSet = mutableSetOf<AutoCloseable>()
|
val autoCloseableSet = mutableSetOf<AutoCloseable>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Runtime.getRuntime().addShutdownHook(Thread({
|
Runtime.getRuntime().addShutdownHook(Thread(this::doCloseResources, "Shutdown-AutoCloseable"))
|
||||||
log.debug { "Closing registered hook resources..." }
|
}
|
||||||
autoCloseableSet.forEach {
|
|
||||||
try {
|
fun doCloseResources() {
|
||||||
it.close()
|
log.debug { "Closing registered hook resources..." }
|
||||||
} catch (e: Exception) {
|
autoCloseableSet.removeIf {
|
||||||
log.error(e) { "An exception occurred while closing the resource. (Resource: `$it`)" }
|
try {
|
||||||
}
|
it.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.error(e) { "An exception occurred while closing the resource. (Resource: `$it`)" }
|
||||||
}
|
}
|
||||||
log.debug { "All registered hook resources have been closed." }
|
true
|
||||||
}, "Shutdown-AutoCloseable"))
|
}
|
||||||
|
log.debug { "All registered hook resources have been closed." }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
package net.lamgc.scalabot.util
|
package net.lamgc.scalabot.util
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.justRun
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
import net.lamgc.scalabot.ExtensionPackageFinder
|
import net.lamgc.scalabot.ExtensionPackageFinder
|
||||||
import net.lamgc.scalabot.FinderPriority
|
import net.lamgc.scalabot.FinderPriority
|
||||||
import net.lamgc.scalabot.FinderRules
|
import net.lamgc.scalabot.FinderRules
|
||||||
import net.lamgc.scalabot.FoundExtensionPackage
|
import net.lamgc.scalabot.FoundExtensionPackage
|
||||||
import org.eclipse.aether.artifact.Artifact
|
import org.eclipse.aether.artifact.Artifact
|
||||||
import org.eclipse.aether.artifact.DefaultArtifact
|
import org.eclipse.aether.artifact.DefaultArtifact
|
||||||
|
import org.junit.jupiter.api.assertDoesNotThrow
|
||||||
import org.junit.jupiter.api.assertThrows
|
import org.junit.jupiter.api.assertThrows
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import kotlin.test.Test
|
import kotlin.test.*
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertFalse
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
internal class UtilsKtTest {
|
internal class UtilsKtTest {
|
||||||
|
|
||||||
@ -58,4 +61,59 @@ internal class UtilsKtTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `AutoCloseable shutdown hook`() {
|
||||||
|
val utilsInternalClass = Class.forName("net.lamgc.scalabot.util.UtilsInternal")
|
||||||
|
val utilsInternalObject = utilsInternalClass.getDeclaredField("INSTANCE").get(null)
|
||||||
|
?: fail("无法获取 UtilsInternal 对象.")
|
||||||
|
val doCloseResourcesMethod = utilsInternalClass.getDeclaredMethod("doCloseResources")
|
||||||
|
.apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常的运行过程.
|
||||||
|
val mockResource = mockk<AutoCloseable> {
|
||||||
|
justRun { close() }
|
||||||
|
}.registerShutdownHook()
|
||||||
|
doCloseResourcesMethod.invoke(utilsInternalObject)
|
||||||
|
verify { mockResource.close() }
|
||||||
|
|
||||||
|
// 异常捕获检查.
|
||||||
|
val exceptionMockResource = mockk<AutoCloseable> {
|
||||||
|
every { close() } throws RuntimeException("Expected exception.")
|
||||||
|
}.registerShutdownHook()
|
||||||
|
assertDoesNotThrow("在关闭资源时出现未捕获异常.") {
|
||||||
|
doCloseResourcesMethod.invoke(utilsInternalObject)
|
||||||
|
}
|
||||||
|
verify { exceptionMockResource.close() }
|
||||||
|
|
||||||
|
// 错误抛出检查.
|
||||||
|
val errorMockResource = mockk<AutoCloseable> {
|
||||||
|
every { close() } throws Error("Expected error.")
|
||||||
|
}.registerShutdownHook()
|
||||||
|
assertThrows<Error>("关闭资源时捕获了不该捕获的 Error.") {
|
||||||
|
try {
|
||||||
|
doCloseResourcesMethod.invoke(utilsInternalObject)
|
||||||
|
} catch (e: InvocationTargetException) {
|
||||||
|
throw e.targetException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verify { errorMockResource.close() }
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val resourceSet = utilsInternalClass.getDeclaredMethod("getAutoCloseableSet").invoke(utilsInternalObject)
|
||||||
|
as MutableSet<AutoCloseable>
|
||||||
|
resourceSet.clear()
|
||||||
|
|
||||||
|
val closeRef = mockk<AutoCloseable> {
|
||||||
|
justRun { close() }
|
||||||
|
}
|
||||||
|
resourceSet.add(closeRef)
|
||||||
|
assertTrue(resourceSet.contains(closeRef), "测试用资源虚引用添加失败.")
|
||||||
|
doCloseResourcesMethod.invoke(utilsInternalObject)
|
||||||
|
assertFalse(resourceSet.contains(closeRef), "资源虚引用未从列表中删除.")
|
||||||
|
|
||||||
|
resourceSet.clear()
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user