Merge pull request #1828 from brendandahl/intex-gc

Force collection at every unwind.
This commit is contained in:
Myk Melez 2015-08-20 14:03:52 -07:00
Родитель 2bdfe128f6 74ad2a53ac
Коммит 819eb8b7b0
7 изменённых файлов: 76 добавлений и 38 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -25,7 +25,6 @@ img/icon-512.png
index.html
index.js
style/main.css
native.js
# These are generated by the Java pre-processor. They're generated from
# their *.jpp equivalents. Keep them up-to-date when you add *.jpp files!

Просмотреть файл

@ -189,7 +189,6 @@ PREPROCESS_SRCS = \
./index.js.in \
./main.html.in \
./manifest.webapp.in \
./native.js.in \
./style/main.css.in \
./tests/index.js.in \
$(NULL)

Просмотреть файл

@ -17,3 +17,4 @@
/** @const */ var release: boolean = @RELEASE@;
/** @const */ var profile: number = @PROFILE@;
/** @const */ var profileFormat: string = "@PROFILE_FORMAT@";
/** @const */ var asmJsTotalMemory: number = @ASMJS_TOTAL_MEMORY@;

7
int.ts
Просмотреть файл

@ -619,6 +619,13 @@ module J2ME {
while (unwound.length) {
pending.push(unwound.pop());
}
// Garbage collection is disabled during compiled code which can lead to OOM's if
// we consistently stay in compiled code. Most code unwinds often enough that we can
// force collection here since at the end of an unwind all frames are
// stored back on the heap.
if (getFreeMemory() < Constants.FREE_MEMORY_TARGET) {
ASM._forceCollection();
}
}
/**

Просмотреть файл

@ -49,56 +49,57 @@ LocalMsgConnection.prototype.waitConnection = function() {
}).bind(this));
}
LocalMsgConnection.prototype.copyMessage = function(messageQueue, data) {
LocalMsgConnection.prototype.copyMessage = function(messageQueue, dataAddr) {
var msg = messageQueue.shift();
var data = J2ME.getArrayFromAddr(dataAddr);
for (var i = 0; i < msg.length; i++) {
data[i] = msg.data[i + msg.offset];
}
J2ME.unsetUncollectable(dataAddr);
return msg.length;
}
LocalMsgConnection.prototype.sendMessageToClient = function(data, offset, length) {
this.clientMessages.push(new LocalMsgConnectionMessage(data, offset, length));
LocalMsgConnection.prototype.sendMessageToClient = function(dataAddr, offset, length) {
this.clientMessages.push(new LocalMsgConnectionMessage(dataAddr, offset, length));
if (this.clientWaiting.length > 0) {
this.clientWaiting.shift()();
}
}
LocalMsgConnection.prototype.getClientMessage = function(data) {
return this.copyMessage(this.clientMessages, data);
LocalMsgConnection.prototype.getClientMessage = function(dataAddr) {
return this.copyMessage(this.clientMessages, dataAddr);
}
LocalMsgConnection.prototype.waitClientMessage = function(data) {
LocalMsgConnection.prototype.waitClientMessage = function(dataAddr) {
asyncImpl("I", new Promise((function(resolve, reject) {
this.clientWaiting.push(function() {
resolve(this.getClientMessage(data));
resolve(this.getClientMessage(dataAddr));
}.bind(this));
}).bind(this)));
}
LocalMsgConnection.prototype.sendMessageToServer = function(data, offset, length) {
this.serverMessages.push(new LocalMsgConnectionMessage(data, offset, length));
LocalMsgConnection.prototype.sendMessageToServer = function(dataAddr, offset, length) {
this.serverMessages.push(new LocalMsgConnectionMessage(dataAddr, offset, length));
if (this.serverWaiting.length > 0) {
this.serverWaiting.shift()(true);
}
}
LocalMsgConnection.prototype.getServerMessage = function(data) {
return this.copyMessage(this.serverMessages, data);
LocalMsgConnection.prototype.getServerMessage = function(dataAddr) {
return this.copyMessage(this.serverMessages, dataAddr);
}
LocalMsgConnection.prototype.waitServerMessage = function(data) {
LocalMsgConnection.prototype.waitServerMessage = function(dataAddr) {
var ctx = $.ctx;
asyncImpl("I", new Promise((function(resolve, reject) {
this.serverWaiting.push(function(successful) {
if (successful) {
resolve(this.getServerMessage(data));
resolve(this.getServerMessage(dataAddr));
} else {
J2ME.unsetUncollectable(dataAddr);
ctx.setAsCurrentContext();
reject($.newIOException("Client disconnected"));
}
@ -1101,33 +1102,40 @@ Native["org/mozilla/io/LocalMsgConnection.waitConnection.()V"] = function(addr)
};
Native["org/mozilla/io/LocalMsgConnection.sendData.([BII)V"] = function(addr, dataAddr, offset, length) {
// Clone the data since it may be GC'ed.
var dataClone = new Int8Array(length);
var data = J2ME.getArrayFromAddr(dataAddr);
for (var i = 0; i < length; i++) {
dataClone[i] = data[offset + i];
}
var connection = NativeConnectionMap[addr];
var info = NativeMap.get(addr);
if (info.server) {
connection.sendMessageToClient(data, offset, length);
connection.sendMessageToClient(dataClone, 0, dataClone.length);
} else {
if (MIDP.FakeLocalMsgServers.indexOf(info.protocolName) != -1) {
console.warn("sendData (" + util.decodeUtf8(data.subarray(offset, offset + length)) +
console.warn("sendData (" + util.decodeUtf8(dataClone) +
") to an unimplemented localmsg server (" + info.protocolName + ")");
}
connection.sendMessageToServer(data, offset, length);
connection.sendMessageToServer(dataClone, 0, dataClone.length);
}
};
Native["org/mozilla/io/LocalMsgConnection.receiveData.([B)I"] = function(addr, dataAddr) {
var data = J2ME.getArrayFromAddr(dataAddr);
J2ME.setUncollectable(dataAddr);
var connection = NativeConnectionMap[addr];
var info = NativeMap.get(addr);
if (info.server) {
if (connection.serverMessages.length > 0) {
return connection.getServerMessage(data);
return connection.getServerMessage(dataAddr);
}
connection.waitServerMessage(data);
connection.waitServerMessage(dataAddr);
return;
}
@ -1136,8 +1144,8 @@ Native["org/mozilla/io/LocalMsgConnection.receiveData.([B)I"] = function(addr, d
}
if (connection.clientMessages.length > 0) {
return connection.getClientMessage(data);
return connection.getClientMessage(dataAddr);
}
connection.waitClientMessage(data);
connection.waitClientMessage(dataAddr);
};

Просмотреть файл

@ -489,11 +489,11 @@ Native["java/lang/Double.longBitsToDouble.(J)D"] = function(addr, l, h) {
}
Native["java/lang/Runtime.freeMemory.()J"] = function(addr) {
return J2ME.returnLongValue(@ASMJS_TOTAL_MEMORY@ - ASM._getUsedHeapSize());
return J2ME.returnLongValue(J2ME.getFreeMemory());
};
Native["java/lang/Runtime.totalMemory.()J"] = function(addr) {
return J2ME.returnLongValue(@ASMJS_TOTAL_MEMORY@);
return J2ME.returnLongValue(asmJsTotalMemory);
};
Native["java/lang/Runtime.gc.()V"] = function(addr) {

Просмотреть файл

@ -402,8 +402,8 @@ module J2ME {
this.CO = this.classObjectAddresses = Object.create(null);
this.ctx = null;
this.allCtxs = new Set();
this._runtimeId = RuntimeTemplate._nextRuntimeId ++;
this._nextHashCode = this._runtimeId << 24;
this._runtimeId = RuntimeTemplate._nextRuntimeId++;
this._nextHashCode = (this._runtimeId << 24) | 1; // Increase by one so the first hashcode isn't zero.
}
preInitializeClasses(ctx: Context) {
@ -666,6 +666,11 @@ module J2ME {
TWO_PWR_32_DBL = 4294967296,
TWO_PWR_63_DBL = 9223372036854776000,
// The target amount of free memory(in bytes) before garbage collection is forced on unwind.
// Note: This number was chosen somewhat randomly to allow some space for allocation
// during forceCollection, though it has not been verified it needs any space.
FREE_MEMORY_TARGET = 4086,
// The size in bytes of the header in the memory allocated to the object.
OBJ_HDR_SIZE = 8,
@ -773,7 +778,12 @@ module J2ME {
export function getMonitor(ref: number): any {
release || assert(typeof ref === "number", "monitor reference is a number");
return monitorMap[ref] || (monitorMap[ref] = Object.create(null));
var hash = i32[ref + Constants.HASH_CODE_OFFSET >> 2];
if (hash === Constants.NULL) {
hash = i32[ref + Constants.HASH_CODE_OFFSET >> 2] = $.nextHashCode()
}
return monitorMap[hash] || (monitorMap[hash] = Object.create(null));
}
/**
@ -1271,6 +1281,10 @@ module J2ME {
return address;
}
export function getFreeMemory(): number {
return asmJsTotalMemory - ASM._getUsedHeapSize();
}
export function onFinalize(addr: number): void {
NativeMap.delete(addr);
}
@ -1412,17 +1426,27 @@ module J2ME {
return arrayObject;
}
var uncollectableAddress = gcMallocUncollectable(16);
var uncollectableMaxNumber = 4;
var uncollectableNumber = -1;
var uncollectableMaxNumber = 16;
var uncollectableAddress = gcMallocUncollectable(uncollectableMaxNumber << 2);
export function setUncollectable(addr: number) {
uncollectableNumber++;
release || assert(uncollectableNumber < uncollectableMaxNumber, "Max " + uncollectableMaxNumber + " calls to setUncollectable at a time");
i32[(uncollectableAddress >> 2) + uncollectableNumber] = addr;
for (var i = 0; i < uncollectableMaxNumber; i++) {
var address = (uncollectableAddress >> 2) + i;
if (i32[address] === Constants.NULL) {
i32[address] = addr;
return;
}
}
release || Debug.assertUnreachable("There must be a free slot.");
}
export function unsetUncollectable(addr: number) {
i32[(uncollectableAddress >> 2) + uncollectableNumber] = 0;
uncollectableNumber--;
for (var i = 0; i < uncollectableMaxNumber; i++) {
var address = (uncollectableAddress >> 2) + i;
if (i32[address] === addr) {
i32[address] = Constants.NULL;
return;
}
}
release || Debug.assertUnreachable("The adddress was not found in the uncollectables.");
}
export function newArray(elementClassInfo: ClassInfo, size: number): number {