Skip to content

Commit 7efd329

Browse files
committed
Remove unnecessary String.new << ....
1 parent b1c5ad5 commit 7efd329

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

lib/memory/cache.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ def lookup_class_name(klass)
6363
# @parameter obj [String] The string object to cache.
6464
# @returns [String] A cached copy of the string (truncated to 64 characters).
6565
def lookup_string(obj)
66-
# This string is shortened to 64 characters which is what the string report shows. The string report can still list unique strings longer than 64 characters separately because the object_id of the shortened string will be different.
67-
@string_cache[obj] ||= String.new << obj[0, 64]
66+
# This string is shortened to 64 characters which is what the string report shows. The string report (by value) can still list unique strings longer than 64 characters separately because the `object_id` of the shortened string will be different.
67+
@string_cache[obj] ||= obj[0, 64]
6868
rescue RuntimeError => error
6969
# It is possible for the String to be temporarily locked from another Fiber which raises an error when we try to use it as a hash key. i.e: `Socket#read` locks a buffer string while reading data into it. In this case we `#dup`` the string to get an unlocked copy.
7070
if error.message == "can't modify string; temporarily locked"
71-
@string_cache[obj.dup] ||= String.new << obj[0, 64]
71+
@string_cache[obj.dup] ||= obj[0, 64]
7272
else
7373
raise
7474
end

test/memory/sampler.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,48 @@ class MyThing
5959
memory.report # buffer string is locked while reading ObjectSpace#each_object
6060
end
6161

62+
it "does not retain string references" do
63+
# Ruby 3.2 has different shared string behaviour:
64+
skip_unless_minimum_ruby_version("3.3")
65+
66+
# Strings longer than 23 characters share a reference to a "shared" frozen string which should also be GC'd
67+
sampler.run do
68+
5.times do |i|
69+
short_text = "SHORT TEXT ##{i}"
70+
short_text.dup
71+
72+
long_text = "LONG TEXT ##{i} 12345678901234567890123456789012345678901234567890"
73+
long_text.dup
74+
75+
very_long_text = "VERY LONG TEXT ##{i} 12345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890"
76+
very_long_text.dup
77+
78+
# Prevent the last frozen string from being the return value of the block:
79+
nil
80+
end
81+
end
82+
83+
# 30 strings should be allocated (5 iterations * 6 strings per iteration):
84+
# - short_text (interpolated result)
85+
# - short_text.dup
86+
# - long_text (interpolated result)
87+
# - long_text.dup
88+
# - very_long_text (interpolated result)
89+
# - very_long_text.dup
90+
expect(sampler.allocated.size).to be == 30
91+
92+
# Get unique string values:
93+
string_allocations = sampler.allocated.select{|a| a.class_name == "String"}
94+
unique_strings = string_allocations.group_by(&:value).size
95+
96+
# 15 unique strings (5 iterations * 3 unique strings per iteration):
97+
expect(unique_strings).to be == 15
98+
99+
# No strings should be retained (all were eligible for GC):
100+
retained_strings = string_allocations.select(&:retained)
101+
expect(retained_strings.size).to be == 0
102+
end
103+
62104
with "#as_json" do
63105
it "returns allocation count" do
64106
x = nil

0 commit comments

Comments
 (0)