Presize TemporaryBuffer.Heap to a reasonable size
JGit's TemporaryBuffer.Heap recently gained an estimatedSize argument used for presizing the backing block pointer list. Without this argument, it assumes callers are going to use the entire maximum size, and allocates an array large enough to hold enough pointers to 8192-byte blocks to fill the maximum size. This is pathologically wasteful in RestApiServlet, where we might allocate one or more buffers with size Integer.MAX_VALUE just to store JSON serialization results. This allocates an array with over 250k elements, taking about 2M of memory which is immediately made garbage. On *every* request. Most JSON responses are small. We can even be pretty liberal and guess that they are under 10 blocks (80 KiB) in size, costing us only 10 object pointers (<100 bytes) worth of memory. Change-Id: I9e1806737706ab2a24693455d64caa7d1067af35
This commit is contained in:
@@ -142,6 +142,8 @@ public class RestApiServlet extends HttpServlet {
|
||||
private static final String JSON_TYPE = "application/json";
|
||||
private static final String FORM_TYPE = "application/x-www-form-urlencoded";
|
||||
|
||||
private static final int HEAP_EST_SIZE = 10 * 8 * 1024; // Presize 10 blocks.
|
||||
|
||||
/**
|
||||
* Garbage prefix inserted before JSON output to prevent XSSI.
|
||||
* <p>
|
||||
@@ -656,7 +658,7 @@ public class RestApiServlet extends HttpServlet {
|
||||
Multimap<String, String> config,
|
||||
Object result)
|
||||
throws IOException {
|
||||
TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE);
|
||||
TemporaryBuffer.Heap buf = heap(HEAP_EST_SIZE, Integer.MAX_VALUE);
|
||||
buf.write(JSON_MAGIC);
|
||||
Writer w = new BufferedWriter(new OutputStreamWriter(buf, UTF_8));
|
||||
Gson gson = newGson(config, req);
|
||||
@@ -781,7 +783,7 @@ public class RestApiServlet extends HttpServlet {
|
||||
|
||||
private static BinaryResult stackJsonString(HttpServletResponse res,
|
||||
final BinaryResult src) throws IOException {
|
||||
TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE);
|
||||
TemporaryBuffer.Heap buf = heap(HEAP_EST_SIZE, Integer.MAX_VALUE);
|
||||
buf.write(JSON_MAGIC);
|
||||
try(Writer w = new BufferedWriter(new OutputStreamWriter(buf, UTF_8));
|
||||
JsonWriter json = new JsonWriter(w)) {
|
||||
@@ -1053,10 +1055,15 @@ public class RestApiServlet extends HttpServlet {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int base64MaxSize(long n) {
|
||||
return 4 * IntMath.divide((int) n, 3, CEILING);
|
||||
}
|
||||
|
||||
private static BinaryResult base64(BinaryResult bin)
|
||||
throws IOException {
|
||||
int max = 4 * IntMath.divide((int) bin.getContentLength(), 3, CEILING);
|
||||
TemporaryBuffer.Heap buf = heap(max);
|
||||
int maxSize = base64MaxSize(bin.getContentLength());
|
||||
int estSize = Math.min(base64MaxSize(HEAP_EST_SIZE), maxSize);
|
||||
TemporaryBuffer.Heap buf = heap(estSize, maxSize);
|
||||
OutputStream encoded = BaseEncoding.base64().encodingStream(
|
||||
new OutputStreamWriter(buf, ISO_8859_1));
|
||||
bin.writeTo(encoded);
|
||||
@@ -1066,7 +1073,7 @@ public class RestApiServlet extends HttpServlet {
|
||||
|
||||
private static BinaryResult compress(BinaryResult bin)
|
||||
throws IOException {
|
||||
TemporaryBuffer.Heap buf = heap(20 << 20);
|
||||
TemporaryBuffer.Heap buf = heap(HEAP_EST_SIZE, 20 << 20);
|
||||
GZIPOutputStream gz = new GZIPOutputStream(buf);
|
||||
bin.writeTo(gz);
|
||||
gz.close();
|
||||
@@ -1083,8 +1090,8 @@ public class RestApiServlet extends HttpServlet {
|
||||
}.setContentLength(buf.length());
|
||||
}
|
||||
|
||||
private static Heap heap(int max) {
|
||||
return new TemporaryBuffer.Heap(max);
|
||||
private static Heap heap(int est, int max) {
|
||||
return new TemporaryBuffer.Heap(est, max);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
|
||||
Reference in New Issue
Block a user