巨大的持久内存分配与 go-ethereum'绑定.新交易员()'?



我正在开发一个与私有以太坊区块链网络交互的REST服务。首先,我将Java与Web3j库和Jersey一起使用。一切都按预期工作,但服务的单个实例(不是 geth 客户端(占用了高达 500MB 的 RAM!由于我需要同时运行多个实例(~40(以进行模拟,因此我想要更轻的实例。

因此,我切换到Go(32位版本,Windows 10(和原始的go-ethereum软件包。但是,我非常惊讶地发现,一旦我调用并存储bind.NewTransactor()的结果,该程序的内存消耗就会上升到~250MB,这将返回一个*TransactOpts。我看了一下消息来源,但我无法解释这种行为。

这是正常的(如果是,为什么?(还是我错过了什么?

这是我的代码:

import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"strings"
)
const WALLET_PATH string = "path/to/json/wallet"
func main() {
data, _ := ioutil.ReadFile(WALLET_PATH)
// up to this point, the program only takes a few MBs of RAM 
auth, _ := bind.NewTransactor(strings.NewReader(string(data)), "mypassphrase")
// and here the allocated memory rises up to ~270MB
_ = auth
}

好吧,我并没有真正找到问题的答案,但是由于我确实找到了解决方法,因此无论如何我都会分享它。

内存分析

正如@MichaelHampton建议的那样,我记忆了我的程序。top 10命令揭示了以下内容:

(pprof) top 10
Showing nodes accounting for 256MB, 99.88% of 256.30MB total
Dropped 15 nodes (cum <= 1.28MB)
flat  flat%   sum%        cum   cum%
256MB 99.88% 99.88%      256MB 99.88%  github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/scrypt.Key
0     0% 99.88%      256MB 99.88%  github.com/ethereum/go-ethereum/accounts/abi/bind.NewTransactor
0     0% 99.88%      256MB 99.88%  github.com/ethereum/go-ethereum/accounts/keystore.DecryptKey
0     0% 99.88%      256MB 99.88%  github.com/ethereum/go-ethereum/accounts/keystore.decryptKeyV3
0     0% 99.88%      256MB 99.88%  github.com/ethereum/go-ethereum/accounts/keystore.getKDFKey
0     0% 99.88%   256.01MB 99.88%  main.main
0     0% 99.88%   256.30MB   100%  runtime.main

如您所见,内存消耗来自包scrypt中的Key函数,该函数由bind.NewTransactor(...)间接调用。从文档中:

密钥

从密码、盐和成本参数派生密钥,返回可用作加密密钥的长度 keyLen 的字节片。

特别是,这对应于从 JSON 钱包文件生成私钥的方式。实际上,该函数本身为加密计算分配了大量内存。我不明白的是,为什么一旦生成密钥,这种巨大的内存分配似乎仍然存在(当我在之后以log.Fatal(http.ListenAndServe(":8080", nil))启动服务器时,会观察到持久性(。

解决方法

为了证实我的怀疑,我使用了另一种方法来生成我的*TransactOpts,其中包括直接从其十六进制表示形式获取密钥,而不是从钱包文件生成它:

func main() {
privateKey, err := crypto.HexToECDSA("myKeyInHex")
if err != nil {
log.Fatal(err)
}
auth := bind.NewKeyedTransactor(privateKey)
_ = auth
}

尽管bind.NewTransactorbind.NewKeyedTransactor返回完全相同的对象(唯一的区别是密钥的生成方式(,但使用bind.NewTransactor会导致 256MB 的持久内存分配,而不是用于bind.NewKeyedTransactor几 KB,如下所示:

(pprof) top 10
Showing nodes accounting for 11.04kB, 100% of 11.04kB total
Showing top 10 nodes out of 19
flat  flat%   sum%        cum   cum%
6.83kB 61.90% 61.90%     6.83kB 61.90%  time.initLocalFromTZI
4.21kB 38.10%   100%     4.21kB 38.10%  github.com/ethereum/go-ethereum/crypto/sha3.(*state).clone (inline)
0     0%   100%     4.21kB 38.10%  github.com/ethereum/go-ethereum/accounts/abi/bind.NewKeyedTransactor
0     0%   100%     4.21kB 38.10%  github.com/ethereum/go-ethereum/crypto.Keccak256
0     0%   100%     4.21kB 38.10%  github.com/ethereum/go-ethereum/crypto.PubkeyToAddress
0     0%   100%     4.21kB 38.10%  github.com/ethereum/go-ethereum/crypto/sha3.(*state).Sum
0     0%   100%     6.83kB 61.90%  github.com/pkg/profile.Start
0     0%   100%     6.83kB 61.90%  github.com/pkg/profile.Start.func2
0     0%   100%     6.83kB 61.90%  log.(*Logger).Output
0     0%   100%     6.83kB 61.90%  log.(*Logger).formatHeader

因此,对于我的模拟,我将从我的 JSON 钱包生成我的私钥并将它们存储在上游的文本文件中,然后使用bind.NewKeyedTransactor(...).我知道这无论如何都不安全,但对于我的模拟目的来说,这就足够了。

但是,我很确定bind.NewTransactor没有有关内存消耗的预期行为,因此我将在go-ethereum存储库上打开一个问题。

最新更新