kotlin - CorDapp流测试中的内存泄漏

标签 kotlin garbage-collection jvm out-of-memory corda

在CorDapp的内部,我编写了一个自定义的generateSpend函数(附加在代码段中)。它结合使用tokenSelection.attemptSpendgenerateExitaddTokensToRedeem来花费来自各个发行者的代币来支付一笔金额。

示例:[成本为3木材],由[2奥斯丁发行的尼克持有的木材],[1尼克发行的尼克持有的木材]支付

我的实现中存在明显错误吗?这会导致一个非常奇怪的内存泄漏错误: Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

在单独运行时测试通过,但在一次运行时失败。请注意,这里还有100万种可能出错的地方-我最近使用的是Corda 4.4。

我尝试过的事情:

  • 调整gradle.properties参数以添加以下JVM Args(无影响)
  • Xmx2g
  • XX:MaxPermSize = 512m
  • XX:+ HeapDumpOnOutOfMemoryError
  • XX:HeapDumpPath = / tmp / heapdump -XX:+ UseG1GC

  • 代码和错误:

    /**
     * When a player spends resources in-game, those resources are consumed as inputs to a transaction. The generateInGameSpend
     * method leverages the token-SDK to facilitate the building of transaction proposing the consumption of tokens when they are
     * spent (burned) and not transferred to a counter-party.
     *
     * This method uses the generateExit functionality from the tokenSelection and mutates an input transaction builder in place.
     */
    @Suspendable
    fun generateInGameSpend(
            serviceHub: ServiceHub,
            tb: TransactionBuilder,
            costs: Map<TokenType, Long>,
            holder: Party,
            changeOwner: Party,
            additionalQueryCriteria: QueryCriteria? = null,
            messageToLog: String = "",
            logger: Logger? = null
    ): TransactionBuilder {
        // Create a tokenSelector
        val tokenSelection = TokenSelection(serviceHub)
        // Generate exits for tokens of the appropriate type
        costs.filter { it.value > 0 }.forEach { (type, amount) ->
            val baseCriteria = heldTokenAmountCriteria(type, holder)
            val queryCriteria = additionalQueryCriteria?.let { baseCriteria.and(it) } ?: baseCriteria
            // Get a list of tokens satisfying the costs
            val tokensToSpend = tokenSelection
                    .attemptSpend(amount of type, tb.lockId, queryCriteria)
            // Run checks on the tokens to ensure the proposed transaction is valid
            val notaryToCheck = tokensToSpend.first().state.notary
            check(tokensToSpend.all { it.state.notary == notaryToCheck }) { "You are trying to spend assets with different notaries." }
            check(tokensToSpend.isNotEmpty()) { "Received empty list of states to spend." }
            // Begin to spend tokens to satisfy costs
            var spentAmount = Amount(0, type)
            tokensToSpend
                    .groupBy { it.state.data.issuer }
                    .forEach {
                        val amountOfTokens = it.value.sumTokenStateAndRefs().withoutIssuer()
                        spentAmount = spentAmount.plus(amountOfTokens)
                        val (exitStates, change) = tokenSelection.generateExit(
                        it.value,
                        if (spentAmount.quantity > costs[type]!!) Amount(amount, type) else amountOfTokens,
                        changeOwner)
                        addTokensToRedeem(tb, exitStates, change)
                    }
        }
        // Return the mutated transaction builder
        return tb
    }
    ?[m io.github.classgraph.ClassGraphException: Uncaught exception during scan
        at io.github.classgraph.ClassGraphException.newClassGraphException(ClassGraphException.java:89) ~[classgraph-4.8.41.jar:4.8.41]
        at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1183) ~[classgraph-4.8.41.jar:4.8.41]
        at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1201) ~[classgraph-4.8.41.jar:4.8.41]
        at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1214) ~[classgraph-4.8.41.jar:4.8.41]
        at net.corda.core.internal.ClassGraphUtilsKt.pooledScan(ClassGraphUtils.kt:18) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.internal.ClassLoadingUtilsKt.createInstancesOfClassesImplementing(ClassLoadingUtils.kt:22) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:325) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:297) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at java.util.HashMap.computeIfAbsent(HashMap.java:1127) ~[?:1.8.0_211]
        at java.util.Collections$SynchronizedMap.computeIfAbsent(Collections.java:2672) ~[?:1.8.0_211]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(AttachmentsClassLoader.kt:322) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext$default(AttachmentsClassLoader.kt:318) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.transactions.LedgerTransaction.internalPrepareVerify$core(LedgerTransaction.kt:217) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.transactions.LedgerTransaction.verify(LedgerTransaction.kt:207) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.transactions.TransactionBuilder.verify(TransactionBuilder.kt:559) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:131) ~[workflows-0.1.jar:?]
        at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:33) ~[workflows-0.1.jar:?]
        at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270) ~[corda-node-4.4-SNAPSHOT.jar:?]
        at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46) ~[corda-node-4.4-SNAPSHOT.jar:?]
        at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_211]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_211]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_211]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:1.8.0_211]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_211]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_211]
        at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63) ~[corda-node-4.4-SNAPSHOT.jar:?]
    Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
    ?[37m[INFO] 16:44:20,066 [Mock network] statemachine.StaffedFlowHospital. - Flow error allowed to propagate {actor_id=Only For Testing, actor_owning_identity=O=Mock Company 1, L=London, C=GB, actor_store_id=TEST, fiber-id=10000775, flow-id=e2b0e443-f602-46ff-ad4d-93f666cca877, invocation_id=602cdfcf-3c0b-4537-a8a8-c8101c1dfe22, invocation_timestamp=2019-11-13T21:43:08.318Z, origin=Only For Testing, session_id=602cdfcf-3c0b-4537-a8a8-c8101c1dfe22, session_timestamp=2019-11-13T21:43:08.318Z, thread-id=7658}
    ?[m io.github.classgraph.ClassGraphException: Uncaught exception during scan
        at io.github.classgraph.ClassGraphException.newClassGraphException(ClassGraphException.java:89) ~[classgraph-4.8.41.jar:4.8.41]
        at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1183) ~[classgraph-4.8.41.jar:4.8.41]
        at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1201) ~[classgraph-4.8.41.jar:4.8.41]
        at io.github.classgraph.ClassGraph.scan(ClassGraph.java:1214) ~[classgraph-4.8.41.jar:4.8.41]
        at net.corda.core.internal.ClassGraphUtilsKt.pooledScan(ClassGraphUtils.kt:18) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.internal.ClassLoadingUtilsKt.createInstancesOfClassesImplementing(ClassLoadingUtils.kt:22) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:325) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder$withAttachmentsClassloaderContext$serializationContext$1.apply(AttachmentsClassLoader.kt:297) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at java.util.HashMap.computeIfAbsent(HashMap.java:1127) ~[?:1.8.0_211]
        at java.util.Collections$SynchronizedMap.computeIfAbsent(Collections.java:2672) ~[?:1.8.0_211]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(AttachmentsClassLoader.kt:322) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext$default(AttachmentsClassLoader.kt:318) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.transactions.LedgerTransaction.internalPrepareVerify$core(LedgerTransaction.kt:217) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.transactions.LedgerTransaction.verify(LedgerTransaction.kt:207) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at net.corda.core.transactions.TransactionBuilder.verify(TransactionBuilder.kt:559) ~[corda-core-4.4-SNAPSHOT.jar:?]
        at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:131) ~[workflows-0.1.jar:?]
        at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:33) ~[workflows-0.1.jar:?]
        at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270) ~[corda-node-4.4-SNAPSHOT.jar:?]
        at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46) ~[corda-node-4.4-SNAPSHOT.jar:?]
        at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91) ~[quasar-core-0.7.12_r3-jdk8.jar:0.7.12_r3]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_211]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_211]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_211]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:1.8.0_211]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_211]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_211]
        at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63) ~[corda-node-4.4-SNAPSHOT.jar:?]
    Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded


    测试套件1:

    class RobberFlowTests {
    private val network = MockNetwork(MockNetworkParameters(
            notarySpecs = listOf(MockNetworkNotarySpec(CordaX500Name("Notary", "London", "GB"))),
            networkParameters = testNetworkParameters(minimumPlatformVersion = 4),
            cordappsForAllNodes = listOf(
                    TestCordapp.findCordapp("com.flows"),
                    TestCordapp.findCordapp("com.oracleClientFlows"),
                    TestCordapp.findCordapp("com.contractsAndStates"),
                    TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
                    TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
                    TestCordapp.findCordapp("com.r3.corda.lib.tokens.money")
            )
    )
    )
    private val a = network.createNode(MockNodeParameters())
    private val b = network.createNode(MockNodeParameters())
    private val c = network.createNode(MockNodeParameters())
    private val d = network.createNode(MockNodeParameters())
    private val oracleName = CordaX500Name("Oracle", "New York", "US")
    private val oracle = network.createNode(
            MockNodeParameters(legalName = oracleName).withAdditionalCordapps(
                    listOf(
                            TestCordapp.findCordapp("com.oracleService")
                    )
            )
    )
    
    @Before
    fun setup() {
        network.runNetwork()
    }
    
    @After
    fun tearDown() = network.stopNodes()
    
    @Test
    fun player1IsUnableToMoveTheRobberWhenA7IsNotRolled() {
    
        // Get an identity for each of the players of the game.
        val p1 = a.info.chooseIdentity()
        val p2 = b.info.chooseIdentity()
        val p3 = c.info.chooseIdentity()
        val p4 = d.info.chooseIdentity()
    
        // Issue a game state onto the ledger
        val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
        val futureWithGameState = a.startFlow(gameStateIssueFlow)
        network.runNetwork()
    
        val stxGameState = futureWithGameState.getOrThrow()
    
        // Get a reference to the issued game state
        val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
    
        val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
        val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
        val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
    
        setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
        val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
    
        val deterministicDiceRoll = getDiceRollWithSpecifiedRollValue(3, 2, gameBoardState, oracle)
        val rollDiceFlow = RollDiceFlow(gameBoardState.linearId, deterministicDiceRoll)
        val futureWithDiceRoll = arrayOfAllPlayerNodesInOrder[0].startFlow(rollDiceFlow)
        network.runNetwork()
        futureWithDiceRoll.getOrThrow()
    
        val futureWithMovedRobber = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
        network.runNetwork()
    
        assertFailsWith<TransactionVerificationException.ContractRejection>("The associated dice roll must have a value of 7.") { futureWithMovedRobber.getOrThrow() }
    }
    
    @Test
    fun player1IsAbleToMoveTheRobberWhenA7IsRolled() {
    
        // Get an identity for each of the players of the game.
        val p1 = a.info.chooseIdentity()
        val p2 = b.info.chooseIdentity()
        val p3 = c.info.chooseIdentity()
        val p4 = d.info.chooseIdentity()
    
        // Issue a game state onto the ledger
        val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
        val futureWithGameState = a.startFlow(gameStateIssueFlow)
        network.runNetwork()
    
        val stxGameState = futureWithGameState.getOrThrow()
    
        // Get a reference to the issued game state
        val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
    
        val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
        val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
        val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
    
        setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
    
        val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
    
        val deterministicDiceRoll = getDiceRollWithSpecifiedRollValue(3, 4, gameBoardState, oracle)
        val rollDiceFlow = RollDiceFlow(gameBoardState.linearId, deterministicDiceRoll)
        val futureWithDiceRoll = arrayOfAllPlayerNodesInOrder[0].startFlow(rollDiceFlow)
        network.runNetwork()
        futureWithDiceRoll.getOrThrow()
    
        val futureWithClaimedResources = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
        network.runNetwork()
        val futureWithMovedRobber = futureWithClaimedResources.getOrThrow()
    
        requireThat {
            val outputRobber = futureWithMovedRobber.coreTransaction.outputsOfType<RobberState>().first()
            "Assert that the robber has been moved to the appropriate position" using (outputRobber.hexTileIndex == HexTileIndex(5))
        }
    }
    
    @Test
    fun player1IsAbleToApplyTheRobberAfterMovingIt() {
    
        // Get an identity for each of the players of the game.
        val p1 = a.info.chooseIdentity()
        val p2 = b.info.chooseIdentity()
        val p3 = c.info.chooseIdentity()
        val p4 = d.info.chooseIdentity()
    
        // Issue a game state onto the ledger
        val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
        val futureWithGameState = a.startFlow(gameStateIssueFlow)
        network.runNetwork()
    
        val stxGameState = futureWithGameState.getOrThrow()
    
        // Get a reference to the issued game state
        val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
    
        val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
        val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
        val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
    
        setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
    
        val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
    
        val deterministicDiceRoll = getDiceRollWithSpecifiedRollValue(3, 4, gameBoardState, oracle)
        val rollDiceFlow = RollDiceFlow(gameBoardState.linearId, deterministicDiceRoll)
        val futureWithDiceRoll = arrayOfAllPlayerNodesInOrder[0].startFlow(rollDiceFlow)
        network.runNetwork()
        futureWithDiceRoll.getOrThrow()
    
        val futureWithMovedRobber = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
        network.runNetwork()
        futureWithMovedRobber.getOrThrow()
    
        val futureWithRobberApplied = arrayOfAllPlayerNodesInOrder[0].startFlow(ApplyRobberFlow(gameBoardState.linearId))
        network.runNetwork()
        val txWithAppliedRobber = futureWithRobberApplied.getOrThrow().coreTransaction
    
        val inputRobber = txWithAppliedRobber.outputsOfType<RobberState>().single()
        val outputRobber = txWithAppliedRobber.outputsOfType<RobberState>().single()
    
        requireThat {
            "The robber that was deactivated is the robber that was moved" using (outputRobber.linearId == inputRobber.linearId)
            "The robber has no changed position" using (outputRobber.hexTileIndex == inputRobber.hexTileIndex)
            "The output Robber has been deactivated" using (!outputRobber.active)
        }
    
    }
    
    @Test
    fun aPlayerIsAbleToRemoveAPlayBlockerState() {
    
        // Get an identity for each of the players of the game.
        val p1 = a.info.chooseIdentity()
        val p2 = b.info.chooseIdentity()
        val p3 = c.info.chooseIdentity()
        val p4 = d.info.chooseIdentity()
    
        // Issue a game state onto the ledger
        val gameStateIssueFlow = (SetupGameBoardFlow(p1, p2, p3, p4))
        val futureWithGameState = a.startFlow(gameStateIssueFlow)
        network.runNetwork()
    
        val stxGameState = futureWithGameState.getOrThrow()
    
        // Get a reference to the issued game state
        val gameState = stxGameState.coreTransaction.outputsOfType<GameBoardState>().single()
    
        val arrayOfAllTransactions = arrayListOf<SignedTransaction>()
        val arrayOfAllPlayerNodes = arrayListOf(a, b, c, d)
        val arrayOfAllPlayerNodesInOrder = gameState.players.map { player -> arrayOfAllPlayerNodes.filter { it.info.chooseIdentity() == player }.first() }
    
        setupGameBoardForTesting(gameState, network, arrayOfAllPlayerNodesInOrder, arrayOfAllTransactions)
    
        val gameBoardState = arrayOfAllTransactions.last().coreTransaction.outRefsOfType<GameBoardState>().first().state.data
        val nodeWithMoreThan7 = gatherResourcesUntilAPlayerHasMoreThan7(gameBoardState, arrayOfAllPlayerNodesInOrder, oracle, network)
    
        val diceRollTriggeringRobber = getDiceRollWithSpecifiedRollValue(3,4, gameBoardState, oracle)
        val futureWithRobberTriggered = arrayOfAllPlayerNodes[0].startFlow(RollDiceFlow(gameBoardState.linearId, diceRollTriggeringRobber))
        network.runNetwork()
        futureWithRobberTriggered.getOrThrow()
    
        val futureWithMovedRobber = arrayOfAllPlayerNodesInOrder[0].startFlow(MoveRobberFlow(gameBoardState.linearId, 5))
        network.runNetwork()
        futureWithMovedRobber.getOrThrow()
    
        val futureWithRobberApplied = arrayOfAllPlayerNodesInOrder[0].startFlow(ApplyRobberFlow(gameBoardState.linearId))
        network.runNetwork()
        val txWithAppliedRobber = futureWithRobberApplied.getOrThrow().coreTransaction
    
       val playBlockerState = txWithAppliedRobber.outputsOfType<PlayBlockerState>()
                .filter { it.playerBlocked == nodeWithMoreThan7.info.legalIdentities.first() }
                .first()
    
        var resourceTotal = 0
        val resourcesToSpend = mutableMapOf<TokenType, Long>()
        val playerResources = countAllResourcesForASpecificNode(nodeWithMoreThan7).mutableMap
        playerResources.forEach {
            if (resourceTotal < playBlockerState.price) {
                if (resourceTotal + it.value > playBlockerState.price) {
                    val amount = it.value + resourceTotal.toLong() - playBlockerState.price
                    resourcesToSpend[it.key] = amount
                    resourceTotal += amount.toInt()
                }
                else {
                    resourcesToSpend[it.key] = it.value
                    resourceTotal += it.value.toInt()
                }
            }
        }
    
        val futureWithRemovedPlayBlockerState = nodeWithMoreThan7.startFlow(RemovePlayBlockerFlow(playBlockerState.linearId, resourcesToSpend))
        network.runNetwork()
        futureWithRemovedPlayBlockerState.getOrThrow()
    
        requireThat {
            "All nodes now recognize that the nodeWithMoreThan7 has removed its playBlocker" using (
                    arrayOfAllPlayerNodes.all { it.services.vaultService.queryBy<PlayBlockerState>().states.filter { it.state.data.playerBlocked == nodeWithMoreThan7.info.legalIdentities.first() }.isEmpty() })
        }
    
    }
    
    }

    最佳答案

    generateInGameSpend函数移至Corda服务中,因此永远不会检查代码。

    由于流的堆栈不断增长,在运行检查点流时,具有大/长运行循环可能会导致问题。我认为在持久保存检查点/稍后再加载它时可能会引起问题。

    Corda服务内部的代码没有检查点,因此可以避免此问题。

    关于kotlin - CorDapp流测试中的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58859739/

    相关文章:

    java - 获取花在垃圾收集器上的时间

    java - 如何将新的 JVM 附加到生成的 Python 进程?

    java - 网络安全配置 : Using Network Security Config from resource network_security_config debugBuild: true

    用于垃圾收集的java命令行

    基于 JVM 堆内存的 Kubernetes HPA

    c# - 托管堆 OutOfMemory

    java - 查找谁创建了未删除的文件

    android - 使用后退按钮返回上一个 fragment 后如何更改前一个 fragment 的 View ?

    java - 如何在android中构建一个不断增长的表单

    Android studio Unresolved 懒惰引用(kotlin)