Skip to content

Commit 7451962

Browse files
author
Brian Burkhalter
committedFeb 8, 2021
8129776: The optimized Stream returned from Files.lines should unmap the mapped byte buffer (if created) when closed
Reviewed-by: rriggs, psandoz, alanb
1 parent ad525bc commit 7451962

File tree

3 files changed

+50
-18
lines changed

3 files changed

+50
-18
lines changed
 

‎src/java.base/share/classes/java/nio/MappedByteBuffer.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
3232
import jdk.internal.access.foreign.MemorySegmentProxy;
3333
import jdk.internal.access.foreign.UnmapperProxy;
3434
import jdk.internal.misc.ScopedMemoryAccess;
35+
import jdk.internal.misc.Unsafe;
3536

3637

3738
/**
@@ -132,7 +133,7 @@ public boolean isSync() {
132133

133134
@Override
134135
public void unmap() {
135-
throw new UnsupportedOperationException();
136+
Unsafe.getUnsafe().invokeCleaner(MappedByteBuffer.this);
136137
}
137138
} : null;
138139
}

‎src/java.base/share/classes/java/nio/file/FileChannelLinesSpliterator.java

+41-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -39,8 +39,12 @@
3939
import java.util.HashSet;
4040
import java.util.Set;
4141
import java.util.Spliterator;
42+
import java.util.concurrent.atomic.AtomicInteger;
4243
import java.util.function.Consumer;
4344

45+
import jdk.internal.access.SharedSecrets;
46+
import jdk.internal.access.JavaNioAccess;
47+
4448
/**
4549
* A file-based lines spliterator, leveraging a shared mapped byte buffer and
4650
* associated file channel, covering lines of a file for character encodings
@@ -84,19 +88,31 @@ final class FileChannelLinesSpliterator implements Spliterator<String> {
8488
// Non-null when traversing
8589
private BufferedReader reader;
8690

91+
// Number of references to the shared mapped buffer. Initialized to unity
92+
// when the buffer is created by the root spliterator. Incremented in the
93+
// sub-spliterator constructor. Decremented when 'buffer' transitions from
94+
// non-null to null, either when traversing begins or if the spliterator is
95+
// closed before traversal. If the count is zero after decrementing, then
96+
// the buffer is unmapped.
97+
private final AtomicInteger bufRefCount;
98+
8799
FileChannelLinesSpliterator(FileChannel fc, Charset cs, int index, int fence) {
88100
this.fc = fc;
89101
this.cs = cs;
90102
this.index = index;
91103
this.fence = fence;
104+
this.bufRefCount = new AtomicInteger();
92105
}
93106

94-
private FileChannelLinesSpliterator(FileChannel fc, Charset cs, int index, int fence, ByteBuffer buffer) {
107+
private FileChannelLinesSpliterator(FileChannel fc, Charset cs, int index,
108+
int fence, ByteBuffer buffer, AtomicInteger bufRefCount) {
95109
this.fc = fc;
96-
this.buffer = buffer;
97110
this.cs = cs;
98111
this.index = index;
99112
this.fence = fence;
113+
this.buffer = buffer;
114+
this.bufRefCount = bufRefCount;
115+
this.bufRefCount.incrementAndGet();
100116
}
101117

102118
@Override
@@ -167,7 +183,7 @@ public void close() throws IOException {
167183
private String readLine() {
168184
if (reader == null) {
169185
reader = getBufferedReader();
170-
buffer = null;
186+
unmap();
171187
}
172188

173189
try {
@@ -178,13 +194,6 @@ private String readLine() {
178194
}
179195

180196
private ByteBuffer getMappedByteBuffer() {
181-
// TODO can the mapped byte buffer be explicitly unmapped?
182-
// It's possible, via a shared-secret mechanism, when either
183-
// 1) the spliterator starts traversing, although traversal can
184-
// happen concurrently for mulitple spliterators, so care is
185-
// needed in this case; or
186-
// 2) when the stream is closed using some shared holder to pass
187-
// the mapped byte buffer when it is created.
188197
try {
189198
return fc.map(FileChannel.MapMode.READ_ONLY, 0, fence);
190199
} catch (IOException e) {
@@ -201,6 +210,7 @@ public Spliterator<String> trySplit() {
201210
ByteBuffer b;
202211
if ((b = buffer) == null) {
203212
b = buffer = getMappedByteBuffer();
213+
bufRefCount.set(1);
204214
}
205215

206216
final int hi = fence, lo = index;
@@ -246,7 +256,8 @@ public Spliterator<String> trySplit() {
246256

247257
// The left spliterator will have the line-separator at the end
248258
return (mid > lo && mid < hi)
249-
? new FileChannelLinesSpliterator(fc, cs, lo, index = mid, b)
259+
? new FileChannelLinesSpliterator(fc, cs, lo, index = mid,
260+
b, bufRefCount)
250261
: null;
251262
}
252263

@@ -267,4 +278,22 @@ public long getExactSizeIfKnown() {
267278
public int characteristics() {
268279
return Spliterator.ORDERED | Spliterator.NONNULL;
269280
}
281+
282+
private void unmap() {
283+
if (buffer != null) {
284+
ByteBuffer b = buffer;
285+
buffer = null;
286+
if (bufRefCount.decrementAndGet() == 0) {
287+
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
288+
try {
289+
nioAccess.unmapper(b).unmap();
290+
} catch (UnsupportedOperationException ignored) {
291+
}
292+
}
293+
}
294+
}
295+
296+
void close() {
297+
unmap();
298+
}
270299
}

‎src/java.base/share/classes/java/nio/file/Files.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -4121,9 +4121,11 @@ private static Stream<String> createFileChannelLinesStream(FileChannel fc, Chars
41214121
// FileChannel.size() may in certain circumstances return zero
41224122
// for a non-zero length file so disallow this case.
41234123
if (length > 0 && length <= Integer.MAX_VALUE) {
4124-
Spliterator<String> s = new FileChannelLinesSpliterator(fc, cs, 0, (int) length);
4125-
return StreamSupport.stream(s, false)
4126-
.onClose(Files.asUncheckedRunnable(fc));
4124+
FileChannelLinesSpliterator fcls =
4125+
new FileChannelLinesSpliterator(fc, cs, 0, (int) length);
4126+
return StreamSupport.stream(fcls, false)
4127+
.onClose(Files.asUncheckedRunnable(fc))
4128+
.onClose(() -> fcls.close());
41274129
}
41284130
} catch (Error|RuntimeException|IOException e) {
41294131
try {

0 commit comments

Comments
 (0)
Please sign in to comment.