-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Investigate memory leak in field initializers #122
Comments
I see your point. So is the solution simply to apply the GC transformation to field initialisers? If we do that the field initialisers for static TVP _Z17fieldInitializer4EV(ACLASS this) {
TVP embeding_1 = newIntGC(3, NULL);
TVP ret_1 = CALL_FUNC_PTR(A, A, this, CLASS_A__Z1fEN, embeding_1);
return ret_1;
} Another thing: I noticed that the field initialiser is invoked in the following way: |
Only applying the GC transformation for initializers will not work because the GC will collect them when it's called, whereas we want them to persist. But applying the GC transformation and also wrapping in a deep copy I think will work. It's only a matter of using |
OK, issue fixed in static TVP _Z17fieldInitializer4EV(ACLASS this) {
TVP embeding_1 = newIntGC(3, NULL);
TVP ret_1 = CALL_FUNC_PTR(A, A, this, CLASS_A__Z1fEN, embeding_1);
return vdmClone(ret_1);
} Essentially what happens is that the garbage collection transformation is applied to field initializer. In addition to that, expressions that are returned in field initializers are cloned. @bandurvp please confirm that the generated code looks alright. |
Hi @peterwvj , I've discovered a bug that I think is a result of this fix. In this model
The assignment to g_B_a = _Z1AEV(NULL); but now it is g_B_a = _Z1AEVGC(NULL); Notice also that the second |
Yeah, I see what you mean. Is the solution simply to clone the right-hand-side of assignments when fields (instance + static instance variables) are assigned to? That is, the code should be generated as:
Can you confirm that this is the solution @bandurvp ? I mean, it's not safe to just revert the "fix" (changing it back to "Z1AEV(NULL)") because then the following model snippet will produce code that leaks:
|
Hi @peterwvj , that would work, but it would introduce another layer of cloning. Could an analysis be done and omit the GC versions in assignments to fields because they need to persist for the lifetime of the object? |
Hi @bandurvp Yeah, we could, but is that really a good solution? I mean, the example above (or below) would cause a leak the second time
The other approach would introduce another layer of cloning (that's true), but it would be correct. Do you see what I mean? |
Hi @peterwvj , in normal field assignments, the macros vdmFree(g_B_a);
g_B_a = _Z1AEV(NULL); |
Can you clarify what you mean by "normal field assignment". Is |
Sorry, that wasn't very clear of me. What I meant is that non-static fields receive assignments like this: TVP field_tmp_1 = _Z1AEVGC(NULL);
SET_FIELD_PTR(B, B, this, b, field_tmp_1);
vdmFree(field_tmp_1); where the macro |
Thanks for clarifying. Okay, so the transformation will only cover static fields. I guess this transformation should ignore field initializers? Are there more things to ignore? Now, just to make sure I understand, we want the following bit of code g_B_a = _Z1AEVGC(NULL); to be generated as vdmFree(g_B_a);
g_B_a = _Z1AEV(NULL); Sure, we can do that. I don't think it's that difficult. What about nested values? I mean, what if the assignment in the VDM model looks like vdmFree(g_B_a);
g_B_a = _Z1AEV(NULL, newInt(42)); But does this approach work if the argument is the result produced by (say) a function? For example, let's say that the assignment to generate is |
Agreed that the initial implementation of this should be: vdmFree(g_B_a);
g_B_a = vdmClone(_Z1AEVGC(NULL, newIntGC(42))); |
Hi @bandurvp . There's a fix in |
Currently the GC transformation is not applied at all for field initializers. This seems to result in memory leaks if a field is initialized with, for instance, the result of a function call such as
a := add(1, 2);
The resulting generated code will containnewInt(1)
andnewInt(2)
which can not be reclaimed by the garbage collector or the corresponding call to the enclosing classfree
function. If the parameters ofadd
appear asnewIntGC(1, NULL)
andnewIntGC(2, NULL)
then the memory can be reclaimed.The text was updated successfully, but these errors were encountered: