Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitcbdaee6

Browse files
committed
Update formatting, documentation and limit GC test write frequency
The GarbageCollectionTest performing 1,000 GC operations caused asubstantial increase in test execution time. Trial and error foundcommenting out the new ReferenceUtil.reachabilityFence0 referencesresulted in the failures being consistently detected with a value of250. Nevertheless even a value of 250 would result in the new testbeing approximately 3 times longer in duration than any other test. Toresolve this the default value was reduced to 50 and a system propertyintroduced to allow any arbitrary value to be used. In turn the GitHubAction was amended to test with 1,000 GC-inducing writes as per theoriginal code.As an aside on OpenJDK Java 20 the mere presence of a call toReferenceUtil.reachabilityFence0 was sufficient to pass thetests (even with high values such as 2,000) despite there being nomethod body remaining in reachabilityFence0. As such it is unverifiedwhether the synchronization is actually required for the strongreachability. This is consistent withapache/datasketches-memory#91 (comment).In any event synchronization has been retained given it is the moreconservative approach.
1 parentc5e02f7 commitcbdaee6

File tree

3 files changed

+116
-98
lines changed

3 files changed

+116
-98
lines changed

‎.github/workflows/maven.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
run:./cross-compile.sh
3030

3131
-name:Build with Maven
32-
run:mvn -B verify
32+
run:mvn -B verify -DgcRecordWrites=1000
3333

3434
-name:Store built native libraries for later jobs
3535
uses:actions/upload-artifact@v3

‎src/main/java/org/lmdbjava/ReferenceUtil.java‎

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,34 +22,46 @@
2222

2323
importedu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2424

25+
/**
26+
* Supports creating strong references in manner compatible with Java 8.
27+
*/
2528
publicfinalclassReferenceUtil {
26-
/**
27-
* Ensures that the object referenced by the given reference remains
28-
* <a href="package-summary.html#reachability"><em>strongly reachable</em></a>,
29-
* regardless of any prior actions of the program that might otherwise cause
30-
* the object to become unreachable; thus, the referenced object is not
31-
* reclaimable by garbage collection at least until after the invocation of
32-
* this method.
33-
*
34-
* <p> Recent versions of the JDK have a nasty habit of prematurely deciding objects are unreachable.
35-
* see: https://stackoverflow.com/questions/26642153/finalize-called-on-strongly-reachable-object-in-java-8
36-
* The Java 9 method Reference.reachabilityFence offers a solution to this problem.
37-
*
38-
* <p> This method is always implemented as a synchronization on {@code ref}, not as
39-
* {@code Reference.reachabilityFence} for consistency across platforms and to allow building on JDK 6-8.
40-
* <b>It is the caller's responsibility to ensure that this synchronization will not cause deadlock.</b>
41-
*
42-
* @param ref the reference. If {@code null}, this method has no effect.
43-
* @see https://github.com/netty/netty/pull/8410
44-
*/
45-
@SuppressFBWarnings({"ESync_EMPTY_SYNC","UC_USELESS_VOID_METHOD"})
46-
publicstaticvoidreachabilityFence0(Objectref) {
47-
if (ref !=null) {
48-
synchronized (ref) {
49-
// Empty synchronized is ok: https://stackoverflow.com/a/31933260/1151521
50-
}
51-
}
52-
}
5329

54-
privateReferenceUtil() {}
30+
privateReferenceUtil() {
31+
}
32+
33+
/**
34+
* Ensures that the object referenced by the given reference remains
35+
* <em>strongly reachable</em>, regardless of any prior actions of the program
36+
* that might otherwise cause the object to become unreachable. Thus, the
37+
* referenced object is not reclaimable by garbage collection at least until
38+
* after the invocation of this method.
39+
*
40+
* <p>
41+
* Recent versions of the JDK have a nasty habit of prematurely deciding
42+
* objects are unreachable (eg
43+
* <a href="https://tinyurl.com/so26642153">StackOverflow question
44+
* 26642153</a>.
45+
*
46+
* <p>
47+
* <code>java.lang.ref.Reference.reachabilityFence</code> offers a solution to
48+
* this problem, but it was only introduced in Java 9. LmdbJava presently
49+
* supports Java 8 and therefore this method provides an alternative.
50+
*
51+
* <p>
52+
* This method is always implemented as a synchronization on {@code ref}.
53+
* <b>It is the caller's responsibility to ensure that this synchronization
54+
* will not cause deadlock.</b>
55+
*
56+
* @param ref the reference (null is acceptable but has no effect)
57+
* @see <a href="https://github.com/netty/netty/pull/8410">Netty PR 8410</a>
58+
*/
59+
@SuppressFBWarnings({"ESync_EMPTY_SYNC","UC_USELESS_VOID_METHOD"})
60+
publicstaticvoidreachabilityFence0(finalObjectref) {
61+
if (ref !=null) {
62+
synchronized (ref) {
63+
// Empty synchronized is ok: https://stackoverflow.com/a/31933260/1151521
64+
}
65+
}
66+
}
5567
}

‎src/test/java/org/lmdbjava/GarbageCollectionTest.java‎

Lines changed: 75 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -20,92 +20,98 @@
2020

2121
packageorg.lmdbjava;
2222

23+
importstaticjava.nio.ByteBuffer.allocateDirect;
24+
importstaticjava.nio.charset.StandardCharsets.UTF_8;
25+
importstaticorg.junit.Assert.fail;
26+
importstaticorg.lmdbjava.DbiFlags.MDB_CREATE;
27+
importstaticorg.lmdbjava.Env.create;
28+
29+
importjava.io.File;
30+
importjava.io.IOException;
31+
importjava.nio.ByteBuffer;
32+
2333
importedu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2434
importorg.junit.Rule;
2535
importorg.junit.Test;
2636
importorg.junit.rules.TemporaryFolder;
2737
importorg.mockito.MockedStatic;
2838
importorg.mockito.Mockito;
2939

30-
importjava.io.File;
31-
importjava.io.IOException;
32-
importjava.nio.ByteBuffer;
33-
34-
importstaticjava.nio.ByteBuffer.allocateDirect;
35-
importstaticjava.nio.charset.StandardCharsets.UTF_8;
36-
importstaticorg.junit.Assert.fail;
37-
importstaticorg.lmdbjava.DbiFlags.MDB_CREATE;
38-
importstaticorg.lmdbjava.Env.create;
39-
4040
@SuppressFBWarnings({"DM_GC","RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT"})
4141
@SuppressWarnings("PMD.DoNotCallGarbageCollectionExplicitly")
4242
publicclassGarbageCollectionTest {
4343

44-
privatestaticfinalStringDB_NAME ="my DB";
45-
privatestaticfinalStringKEY_PREFIX ="Uncorruptedkey";
46-
privatestaticfinalStringVAL_PREFIX ="Uncorruptedval";
44+
privatestaticfinalStringDB_NAME ="my DB";
45+
privatestaticfinalStringKEY_PREFIX ="Uncorruptedkey";
46+
privatestaticfinalStringVAL_PREFIX ="Uncorruptedval";
4747

48-
@Rule
49-
publicfinalTemporaryFoldertmp =newTemporaryFolder();
50-
privatevoidputBuffer(Dbi<ByteBuffer>db,Txn<ByteBuffer>txn,inti) {
51-
ByteBufferkey =allocateDirect(24);
52-
ByteBufferval =allocateDirect(24);
53-
key.put((KEY_PREFIX+i).getBytes(UTF_8)).flip();
54-
val.put((VAL_PREFIX+i).getBytes(UTF_8)).flip();
55-
db.put(txn,key,val);
56-
}
57-
@Test
58-
publicvoidbuffersNotGarbageCollectedTest()throwsIOException {
59-
finalFilepath =tmp.newFolder();
48+
@Rule
49+
publicfinalTemporaryFoldertmp =newTemporaryFolder();
6050

61-
finalEnv<ByteBuffer>env =create()
62-
.setMapSize(2_085_760_999)
63-
.setMaxDbs(1)
64-
.open(path);
65-
finalDbi<ByteBuffer>db =env.openDbi(DB_NAME,MDB_CREATE);
51+
@Test
52+
publicvoidbuffersNotGarbageCollectedTest()throwsIOException {
53+
finalFilepath =tmp.newFolder();
54+
try (Env<ByteBuffer>env =create()
55+
.setMapSize(2_085_760_999)
56+
.setMaxDbs(1)
57+
.open(path)) {
58+
finalDbi<ByteBuffer>db =env.openDbi(DB_NAME,MDB_CREATE);
6659

67-
// Trigger compilation and whatnot
68-
try (Txn<ByteBuffer>txn =env.txnWrite()) {
69-
for (inti =0;i <5000;i++) {
70-
putBuffer(db,txn,i);
71-
}
72-
txn.commit();
60+
try (Txn<ByteBuffer>txn =env.txnWrite()) {
61+
for (inti =0;i <5_000;i++) {
62+
putBuffer(db,txn,i);
7363
}
74-
// Call gc before writing to lmdb and after last reference to buffer by changing the behavior of mask
75-
try (MockedStatic<MaskedFlag>mockedStatic =Mockito.mockStatic(MaskedFlag.class)) {
76-
mockedStatic.when(MaskedFlag::mask).thenAnswer(invocationOnMock -> {
77-
System.gc();
78-
return0;
79-
});
80-
try (Txn<ByteBuffer>txn =env.txnWrite()) {
81-
for (inti =0;i <1000;i++) {
82-
putBuffer(db,txn,i);
83-
}
84-
txn.commit();
85-
}
64+
txn.commit();
65+
}
66+
67+
// Call GC before writing to LMDB and after last reference to buffer by
68+
// changing the behavior of mask
69+
try (MockedStatic<MaskedFlag>mockedStatic =Mockito.mockStatic(
70+
MaskedFlag.class)) {
71+
mockedStatic.when(MaskedFlag::mask).thenAnswer(invocationOnMock -> {
72+
System.gc();
73+
return0;
74+
});
75+
finalintgcRecordWrites =Integer.getInteger("gcRecordWrites",50);
76+
try (Txn<ByteBuffer>txn =env.txnWrite()) {
77+
for (inti =0;i <gcRecordWrites;i++) {
78+
putBuffer(db,txn,i);
79+
}
80+
txn.commit();
8681
}
82+
}
8783

88-
// Find corrupt keys
89-
try (Txn<ByteBuffer>txn =env.txnRead()) {
90-
try (Cursor<ByteBuffer>c =db.openCursor(txn)) {
91-
if (c.first()) {
92-
do {
93-
byte[]rkey =newbyte[c.key().remaining()];
94-
c.key().get(rkey);
95-
byte[]rval =newbyte[c.val().remaining()];
96-
c.val().get(rval);
97-
Stringskey =newString(rkey,UTF_8);
98-
Stringsval =newString(rval,UTF_8);
99-
if (!skey.startsWith("Uncorruptedkey")) {
100-
fail("Found corrupt key " +skey);
101-
}
102-
if (!sval.startsWith("Uncorruptedval")) {
103-
fail("Found corrupt val " +sval);
104-
}
105-
}while (c.next());
106-
}
107-
}
84+
// Find corrupt keys
85+
try (Txn<ByteBuffer>txn =env.txnRead()) {
86+
try (Cursor<ByteBuffer>c =db.openCursor(txn)) {
87+
if (c.first()) {
88+
do {
89+
finalbyte[]rkey =newbyte[c.key().remaining()];
90+
c.key().get(rkey);
91+
finalbyte[]rval =newbyte[c.val().remaining()];
92+
c.val().get(rval);
93+
finalStringskey =newString(rkey,UTF_8);
94+
finalStringsval =newString(rval,UTF_8);
95+
if (!skey.startsWith("Uncorruptedkey")) {
96+
fail("Found corrupt key " +skey);
97+
}
98+
if (!sval.startsWith("Uncorruptedval")) {
99+
fail("Found corrupt val " +sval);
100+
}
101+
}while (c.next());
102+
}
108103
}
109-
env.close();
104+
}
110105
}
106+
}
107+
108+
privatevoidputBuffer(finalDbi<ByteBuffer>db,finalTxn<ByteBuffer>txn,
109+
finalinti) {
110+
finalByteBufferkey =allocateDirect(24);
111+
finalByteBufferval =allocateDirect(24);
112+
key.put((KEY_PREFIX +i).getBytes(UTF_8)).flip();
113+
val.put((VAL_PREFIX +i).getBytes(UTF_8)).flip();
114+
db.put(txn,key,val);
115+
}
116+
111117
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp