mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Currently we use external strings for internalized builtin source code. However when a snapshot is taken, any external string whose resource is not registered is flattened into a SeqString (see ref). The result is that module source code stored in the snapshot does not use external strings after deserialization. This patch registers an external string resource for each internalized builtin's source. The savings are substantial: ~1.9 MB of heap memory per isolate, or ~44% of an otherwise empty isolate's heap usage: ```console $ node --expose-gc -p 'gc(),process.memoryUsage().heapUsed' 4190968 $ ./node --expose-gc -p 'gc(),process.memoryUsage().heapUsed' 2327536 ``` The savings can be even higher for user snapshots which may include more internal modules. The existing UnionBytes implementation was ill-suited, because it only created an external string resource when ToStringChecked was called, but we need to register the external string resources before the isolate even exists. We change UnionBytes to no longer own the data, and shift ownership of the data to a new external resource class called StaticExternalByteResource. StaticExternalByteResource are either statically allocated (for internalized builtin code) or owned by the static `externalized_builtin_sources` map, so they will only be destructed when static resources are destructed. We change JS2C to emit statements to register a string resource for each internalized builtin. Refs: https://github.com/v8/v8/blob/d2c8fbe9ccd1a6ce5591bb7dd319c3c00d6bf489/src/snapshot/serializer.cc#L633 PR-URL: https://github.com/nodejs/node/pull/47055 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
82 lines
2.7 KiB
C++
82 lines
2.7 KiB
C++
|
|
#ifndef SRC_NODE_UNION_BYTES_H_
|
|
#define SRC_NODE_UNION_BYTES_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include "v8.h"
|
|
|
|
namespace node {
|
|
|
|
// An external resource intended to be used with static lifetime.
|
|
template <typename Char, typename IChar, typename Base>
|
|
class StaticExternalByteResource : public Base {
|
|
static_assert(sizeof(IChar) == sizeof(Char),
|
|
"incompatible interface and internal pointers");
|
|
|
|
public:
|
|
explicit StaticExternalByteResource(const Char* data,
|
|
size_t length,
|
|
std::shared_ptr<void> owning_ptr)
|
|
: data_(data), length_(length), owning_ptr_(owning_ptr) {}
|
|
|
|
const IChar* data() const override {
|
|
return reinterpret_cast<const IChar*>(data_);
|
|
}
|
|
size_t length() const override { return length_; }
|
|
|
|
void Dispose() override {
|
|
// We ignore Dispose calls from V8, even if we "own" a resource via
|
|
// owning_ptr_. All instantiations of this class are static or owned by a
|
|
// static map, and will be destructed when static variables are destructed.
|
|
}
|
|
|
|
StaticExternalByteResource(const StaticExternalByteResource&) = delete;
|
|
StaticExternalByteResource& operator=(const StaticExternalByteResource&) =
|
|
delete;
|
|
|
|
private:
|
|
const Char* data_;
|
|
const size_t length_;
|
|
std::shared_ptr<void> owning_ptr_;
|
|
};
|
|
|
|
using StaticExternalOneByteResource =
|
|
StaticExternalByteResource<uint8_t,
|
|
char,
|
|
v8::String::ExternalOneByteStringResource>;
|
|
using StaticExternalTwoByteResource =
|
|
StaticExternalByteResource<uint16_t,
|
|
uint16_t,
|
|
v8::String::ExternalStringResource>;
|
|
|
|
// Similar to a v8::String, but it's independent from Isolates
|
|
// and can be materialized in Isolates as external Strings
|
|
// via ToStringChecked.
|
|
class UnionBytes {
|
|
public:
|
|
explicit UnionBytes(StaticExternalOneByteResource* one_byte_resource)
|
|
: one_byte_resource_(one_byte_resource), two_byte_resource_(nullptr) {}
|
|
explicit UnionBytes(StaticExternalTwoByteResource* two_byte_resource)
|
|
: one_byte_resource_(nullptr), two_byte_resource_(two_byte_resource) {}
|
|
|
|
UnionBytes(const UnionBytes&) = default;
|
|
UnionBytes& operator=(const UnionBytes&) = default;
|
|
UnionBytes(UnionBytes&&) = default;
|
|
UnionBytes& operator=(UnionBytes&&) = default;
|
|
|
|
bool is_one_byte() const { return one_byte_resource_ != nullptr; }
|
|
|
|
v8::Local<v8::String> ToStringChecked(v8::Isolate* isolate) const;
|
|
|
|
private:
|
|
StaticExternalOneByteResource* one_byte_resource_;
|
|
StaticExternalTwoByteResource* two_byte_resource_;
|
|
};
|
|
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_NODE_UNION_BYTES_H_
|