Questions about Avian and iOS port

77 views
Skip to first unread message

Terasgr

unread,
Sep 24, 2016, 11:30:24 AM9/24/16
to Avian
Hello brave Avian team!

I have a few questions (and suggestions) for Avian & especially the iOS port, and I’d like to discuss the with you, if you wish.

First of all, I believe that the option 
    -framework Carbon
is not valid for an iOS application at all. It might be used for (older) SWT implementation, but not even for that any more (now SWT is supposed to use Cocoa).
Btw, the binary SWT demos of Avian do not work any more at all, in OSX, probably exactly due to this missing Carbon library from the OS? Or because they start in the main thread witch is now deprecated and should run in another thread?


Another thing is that I think the options:
-fobjc-abi-version=2 -fobjc-legacy-dispatch
and
-Xlinker -objc_abi_version -Xlinker 2 
are not needed any more. It might be needed in (very) old versions of the simulator but now I have the feeling that they are not.


A third thing, rather trivial, but a bit not-expected. It has to do with core Avian itself.
When compiling avian with the options 
make platform=ios arch=arm64 process=compile mode=fast bootimage=true
under the build directory it is found the "ios-arm64-bootimage” folder (which is expected) and the "macosx-x86_64-interpret” which is not expected.
When creating the bootimage, bootimage-generator requests the “libjvm.dylib” file found there with the “-hostvm" parameter. I have no knowledge what actually this parameter does, but it seems a bit strange to link a mac dylib with an arm64 executable. Things get more tricky, because indeed lambda functions seem to properly work (as expected) on an actual iPhone device.


I am also trying to optimise the procedure, compile Avian only once and use it multiple times when compiling an iOS project.
It seems that 4 files are required:
a) bootimage-generator
b) classpath.jar
c) libavian.a 
d) libjvm.dylib

From what I understand is, that I need (a) and (c) in all architectures (sim i386, sim x86_64, arm, and arm64), while (b) and (d) only once.
Am I correct or I am missing something?


PS:

The main reason of my interest is that, I am the core developer of a multi-platform development environment for mobile devices, which make possible to compile true cross-platform applications, including the native GUI based on iOS libraries, in Java, for iOS, Android and Desktop. ( http://crossmobile.tech if you are curious - we haven’t really released it openly yet, only for our own internal projects)

Right now we are using XMLVM Objective C backend (which in the past I was an active contributor).
Unfortunately now XMLVM has started to show its age, and we are considering to use alternative methods instead. Avian seemed to me the best alternative and I am trying to dive deeply in its guts to see its limits.

The main requirement for us would be a direct way to connect Objective C objects with Java objects. Since I haven’t seen such an approach in the demos, I was thinking to ask you about it. I was thinking to keep a pointer reference of the allocated Objective C object inside the Java object (as long number) and call the release method on the finalize() method of the Java object. And -of course- I need to find a clever way to instantiate Objective C objects from Java objects and Java objects from Objective C objects (XMLVM performs a similar mapping but I am afraid is not directly applicable with Avian).

What do you think about this approach? Do you have any better ideas? Or is there any example that I missed  (even for pure C memory allocations or C++ objects) ?



Thank you for your patience and once more thank you for the beautiful toolkit you provide.

-- 
Panayotis

Joel Dice .

unread,
Sep 26, 2016, 12:29:20 AM9/26/16
to Avian
On Fri, Sep 23, 2016 at 4:55 PM, Terasgr <tera...@gmail.com> wrote:
> Hello brave Avian team!
>
> I have a few questions (and suggestions) for Avian & especially the iOS
> port, and I’d like to discuss the with you, if you wish.
>
> First of all, I believe that the option
> -framework Carbon
> is not valid for an iOS application at all. It might be used for (older) SWT
> implementation, but not even for that any more (now SWT is supposed to use
> Cocoa).
> Btw, the binary SWT demos of Avian do not work any more at all, in OSX,
> probably exactly due to this missing Carbon library from the OS? Or because
> they start in the main thread witch is now deprecated and should run in
> another thread?

I agree that Carbon never made sense for iOS, and would not be
surprised if it's no longer supported on MacOS. Not sure why the demo
apps aren't working, but I'll debug that when I have time.

> Another thing is that I think the options:
> -fobjc-abi-version=2 -fobjc-legacy-dispatch
> and
> -Xlinker -objc_abi_version -Xlinker 2
> are not needed any more. It might be needed in (very) old versions of the
> simulator but now I have the feeling that they are not.

You're probably right. I can't remember what those flags are for,
anyway, and if hello-ios.git still builds and runs okay without them
we might as well remove them.

> A third thing, rather trivial, but a bit not-expected. It has to do with
> core Avian itself.
> When compiling avian with the options
> make platform=ios arch=arm64 process=compile mode=fast bootimage=true
> under the build directory it is found the "ios-arm64-bootimage” folder
> (which is expected) and the "macosx-x86_64-interpret” which is not expected.
> When creating the bootimage, bootimage-generator requests the “libjvm.dylib”
> file found there with the “-hostvm" parameter. I have no knowledge what
> actually this parameter does, but it seems a bit strange to link a mac dylib
> with an arm64 executable. Things get more tricky, because indeed lambda
> functions seem to properly work (as expected) on an actual iPhone device.

This is because the bootimage-generator executable, which is
responsible for AOT-compiling Java bytecode, needs to actually run
some Java code in order to compile any invokedynamic-based lambda
expressions it encounters. This was a design choice I made because I
had already written the needed code in Java and didn't want to rewrite
(and maintain) it in C++ as well. So the makefile builds a minimal
JVM for the native platform (macosx in this case) so it can run that
Java code while generating the bootimage. It can't use the system JVM
(e.g. HotSpot) for this purpose, because the Java code in this case is
closely tied to Avian's internals.

Anyway, yes, it's complicated, but it saved us from duplicating a
bunch of code between Java and C++. If this causes a lot of pain for
you, we could discuss alternatives, but that's the way it works now.

> I am also trying to optimise the procedure, compile Avian only once and use
> it multiple times when compiling an iOS project.
> It seems that 4 files are required:
> a) bootimage-generator
> b) classpath.jar
> c) libavian.a
> d) libjvm.dylib
>
> From what I understand is, that I need (a) and (c) in all architectures (sim
> i386, sim x86_64, arm, and arm64), while (b) and (d) only once.
> Am I correct or I am missing something?

That seems correct to me. Josh has done some work to allow the
bytecode-to-machine-code compiler target more than one architecture in
a single build, which would allow you to build just one instance of
(a) and (c), but I don't think that project was ever entirely
finished. Josh can correct me if I'm wrong about that.

> PS:
>
> The main reason of my interest is that, I am the core developer of a
> multi-platform development environment for mobile devices, which make
> possible to compile true cross-platform applications, including the native
> GUI based on iOS libraries, in Java, for iOS, Android and Desktop. (
> http://crossmobile.tech if you are curious - we haven’t really released it
> openly yet, only for our own internal projects)
>
> Right now we are using XMLVM Objective C backend (which in the past I was an
> active contributor).
> Unfortunately now XMLVM has started to show its age, and we are considering
> to use alternative methods instead. Avian seemed to me the best alternative
> and I am trying to dive deeply in its guts to see its limits.
>
> The main requirement for us would be a direct way to connect Objective C
> objects with Java objects. Since I haven’t seen such an approach in the
> demos, I was thinking to ask you about it. I was thinking to keep a pointer
> reference of the allocated Objective C object inside the Java object (as
> long number) and call the release method on the finalize() method of the
> Java object. And -of course- I need to find a clever way to instantiate
> Objective C objects from Java objects and Java objects from Objective C
> objects (XMLVM performs a similar mapping but I am afraid is not directly
> applicable with Avian).
>
> What do you think about this approach? Do you have any better ideas? Or is
> there any example that I missed (even for pure C memory allocations or C++
> objects) ?

Using finalize() is almost never a good idea in my opinion. Avian
collects garbage generationally, meaning objects which reach the
tenure stage can remain resident long after the last references to
them have been eliminated. Since the VM can't "see" the space taken
up by the corresponding Objective C objects, nor do those objects put
any pressure on the Java heap, it has no urgency to collect and
finalize the objects which reference them. For the same reason, it's
almost always a bad idea to rely on finalize() to clean up e.g. open
file descriptors, OS handles, and other limited resources that have no
direct relationship to the Java heap; the VM doesn't know (and
shouldn't be expected to know) those resources need to be cleaned up
promptly even when there's no other reason to run a GC cycle.

Anyway, to answer your question there isn't any example of a clever
way to tie Objective-C objects to Java objects. In all the
Avian-on-iOS projects I've worked on, we generally kept the Java and
Objective-C components of our apps separate and let them communicate
via a language-agnostic protocol like Protocol Buffers, almost as if
they were separate processes. In the cases where the Java code needed
to refer directly to an Objective-C object, we used a long to store
the pointer like you mentioned, but also made sure to explicitly
release that pointer when we were done with it rather than rely on
finalize() to release it in some indeterminate future.

Unfortunately, Java-style garbage collection doesn't pair well with
reference counting, at least not if you expect references counts to be
decremented promptly or predictably. On the other hand, if you don't
care about promptness or predictability, finalize() might be a
solution, but I would recommend phantom references over finalize()
since it gives you more control, e.g. over which thread the release
call is made from.

XMLVM and j2objc are better at this since they translate Java code to
Objective-C code and (I assume) rely inherently on reference counting
rather than traditional Java-style GC.

Hope that helps clarify things.

Terasgr

unread,
Sep 27, 2016, 2:33:22 PM9/27/16
to Avian

> Btw, the binary SWT demos of Avian do not work any more at all, in OSX,
> probably exactly due to this missing Carbon library from the OS? Or because
> they start in the main thread witch is now deprecated and should run in
> another thread?

Not sure why the demo
apps aren't working, but I'll debug that when I have time.

Maybe "-XstartOnFirstThread" needs to be provided as an argument to the VM?
The idea is SWT event thread should be the 0 thread, while AWT thread should not be the 0 thread.
There is another trick with getNonBlockingMainQueueExecutor
https://coderanch.com/how-to/javadoc/appledoc/api/com/apple/concurrent/Dispatch.html#getNonBlockingMainQueueExecutor()
which should enclose the initial SWT calls.

According to the documentation, if this is not the case, the events will not be able to properly propagete (and this is how it looks like).



Anyway, yes, it's complicated, but it saved us from duplicating a 
bunch of code between Java and C++.  If this causes a lot of pain for
you, we could discuss alternatives, but that's the way it works now.

No it is clear. And a clever approach actually.


> It seems that 4 files are required:
> a) bootimage-generator
> b) classpath.jar
> c) libavian.a
> d) libjvm.dylib
>

That seems correct to me.  Josh has done some work to allow the
bytecode-to-machine-code compiler target more than one architecture in
a single build, which would allow you to build just one instance of
(a) and (c), but I don't think that project was ever entirely
finished.  Josh can correct me if I'm wrong about that.

(c) is easy to make with tools like lipo and ar actually.
 
 
> I was thinking to keep a pointer
> reference of the allocated Objective C object inside the Java object (as
> long number) and call the release method on the finalize() method of the
> Java object.

Using finalize() is almost never a good idea in my opinion.

...
 
First of all, thank you for the thorough explanation.
Unfortunately I think solutions such as "use a close() method or know in advance that an object could be removed soon from memory ( i.e. using phantom reference ) is not applicable.
Think of a UI element: it will be "created" in Java, and "attached" to the UI mechanism, and then the Java part might forget about it. But the UI object is not (yet) ready to be freed. It still lives and probably will be requested again in the future (by other UI methods).
Thus practically we never know in Java, when the object is really removed from memory anyways.

In the meantime, the UI reference might be stored in the Java world and manipulated on demand, making GC almost a necessity.


Unfortunately, Java-style garbage collection doesn't pair well with
reference counting, at least not if you expect references counts to be
decremented promptly or predictably.  On the other hand, if you don't
care about promptness or predictability, finalize() might be a
solution, but I would recommend phantom references over finalize()
since it gives you more control, e.g. over which thread the release
call is made from.


I was thinking of a simple approach, similar to what XMLVM does with their C backend: retain the obejct in the Java constructor and releaseing it when finalizing.

 
XMLVM and j2objc are better at this since they translate Java code to
Objective-C code and (I assume) rely inherently on reference counting
rather than traditional Java-style GC.


Yes, in this case they directly transform Java calls to ObjC calls anyways.
Not with XMLVM C backend though. There boehm GC is used.


Actually, I was wishing for an answer something like: "you can tweak the GC to be more aggresive", or something similar.
If it is possible to direct the GC to perform collecting more often or when I think it's nessecary.
Like OpenJDK's GC strategies.

Is it possible to somehow fine tweak the Avian GC?

Terasgr

unread,
Sep 27, 2016, 8:33:07 PM9/27/16
to Avian
>  On the other hand, if you don't
> care about promptness or predictability, finalize() might be a
> solution, but I would recommend phantom references over finalize()
> since it gives you more control, e.g. over which thread the release
> call is made from.

After a lot of reading and a lot of thought, I think at the end of the day I'll settle with the phantom( or weak) reference approach. Interestingly RoboVM (which has a similar problem with the one I describe here) decided to use the finalize approach and inheritance, not PhantomReference, not even composition-based resource handling.

But after all this reading I am convinced now that your proposal, using Weak/PhantomReference instead is the best one.
Thank you for your support. Once more they are really helpful.

Joel Dice .

unread,
Sep 27, 2016, 11:27:46 PM9/27/16
to Avian
On Tue, Sep 27, 2016 at 6:33 PM, Terasgr <tera...@gmail.com> wrote:
> After a lot of reading and a lot of thought, I think at the end of the day
> I'll settle with the phantom( or weak) reference approach. Interestingly
> RoboVM (which has a similar problem with the one I describe here) decided to
> use the finalize approach and inheritance, not PhantomReference, not even
> composition-based resource handling.
>
> But after all this reading I am convinced now that your proposal, using
> Weak/PhantomReference instead is the best one.
> Thank you for your support. Once more they are really helpful.

Glad it helped. Regarding your earlier comments about making the GC
more aggressive: you can always just call System.gc() at regular
intervals, e.g. from a daemon thread or an idling UI loop. That will
hurt performance if you do it too often, but it will force a major
collection, ensuring all unreachable (and weakly reachable) objects
are disposed of. Other than that, there really isn't a better
solution besides rewriting the whole VM to use reference counting
instead of "lazy" GC. Or, as in the case of SWT, explicit object
lifetime management.

Joel Dice .

unread,
Sep 28, 2016, 10:31:05 AM9/28/16
to Avian
One other thought: if you could somehow keep track of the total memory
footprint of all Objective-C object allocations (i.e. increment it on
allocation and decrement it on deallocation), you could trigger a
System.gc() each time that footprint exceeded a threshold (which could
be adjusted dynamically). For example, you could call System.gc()
each time the footprint doubles relative to its size following the
previous GC.

The tricky bit is how to accurately track that footprint; perhaps
there's an API in the Objective-C runtime to listen for all
allocations and deallocations, or better yet, a function to query the
current heap size.

Terasgr

unread,
Sep 28, 2016, 6:34:07 PM9/28/16
to Avian
Τη Τετάρτη, 28 Σεπτεμβρίου 2016 - 5:31:05 μ.μ. UTC+3, ο χρήστης Joel Dice έγραψε:

One other thought: if you could somehow keep track of the total memory
footprint of all Objective-C object allocations (i.e. increment it on
allocation and decrement it on deallocation), you could trigger a
System.gc() each time that footprint exceeded a threshold (which could
be adjusted dynamically).  For example, you could call System.gc()
each time the footprint doubles relative to its size following the
previous GC.

The tricky bit is how to accurately track that footprint; perhaps
there's an API in the Objective-C runtime to listen for all
allocations and deallocations, or better yet, a function to query the
current heap size.

I can indeed listen to all allocations and deallocations in various ways in ObjC; ObjC provides many ways to override even system functions if you want.
There is a simpler approach though, since allocation is performed always on the constructor and deallocation on the finalizer (or the trick with the references), I can have this metric even in Java.
And since deallocation like this is unpredictable, I could do something like this on the constructor only.

Joel Dice .

unread,
Sep 28, 2016, 7:01:00 PM9/28/16
to Avian
The finalizer (or phantom reference listener) will only call
`release`, right? That won't actually deallocate the object unless
the reference count goes to zero, so you won't know for sure that it's
been deallocated. In other words, the reference count for an ObjC
object may be incremented during its lifetime (e.g. due to being
referenced by other ObjC objects or perhaps even Java objects), so the
finalizer cannot safely deallocate it unless it knows it has the only
remaining reference, i.e. when the reference count is one.

Of course, you could just trigger a GC every N ObjC allocations
regardless of the actual ObjC heap footprint, but the ideal scenario
would be to determine the exact footprint and only force a GC when it
reaches some predefined limit. For example, if you want to keep the
heap under 32MB, you force a GC if and only if the next allocation
would exceed that limit. If you call System.gc() every N allocations
or every N seconds, you'll either do unnecessary work (e.g. when the
footprint is already well under the limit) or miss doing it when you
ought to (e.g. when the footprint exceeds the limit but the next GC is
not due until many more allocations or seconds have passed).

Terasgr

unread,
Sep 29, 2016, 9:43:32 PM9/29/16
to Avian


Τη Πέμπτη, 29 Σεπτεμβρίου 2016 - 2:01:00 π.μ. UTC+3, ο χρήστης Joel Dice έγραψε:

The finalizer (or phantom reference listener) will only call
`release`, right?  That won't actually deallocate the object unless
the reference count goes to zero, so you won't know for sure that it's
been deallocated.  In other words, the reference count for an ObjC
object may be incremented during its lifetime (e.g. due to being
referenced by other ObjC objects or perhaps even Java objects), so the
finalizer cannot safely deallocate it unless it knows it has the only
remaining reference, i.e. when the reference count is one.


Yes, but this is not Java's repsonsibility, right? If something else for any reason wants a reference to the same object, it can have it. As long as Java has relesed the object ASAP when not required any more, it is good enough I think.

Technically it is possible to monitor (and partially replace) any method, even those who live on a library object, with method swizzling, but I don't feel it's necessary.

Joel Dice .

unread,
Sep 30, 2016, 6:31:37 PM9/30/16
to Avian
On Thu, Sep 29, 2016 at 7:43 PM, Terasgr <tera...@gmail.com> wrote:
>
>
> Τη Πέμπτη, 29 Σεπτεμβρίου 2016 - 2:01:00 π.μ. UTC+3, ο χρήστης Joel Dice
> έγραψε:
>>
>>
>> The finalizer (or phantom reference listener) will only call
>> `release`, right? That won't actually deallocate the object unless
>> the reference count goes to zero, so you won't know for sure that it's
>> been deallocated. In other words, the reference count for an ObjC
>> object may be incremented during its lifetime (e.g. due to being
>> referenced by other ObjC objects or perhaps even Java objects), so the
>> finalizer cannot safely deallocate it unless it knows it has the only
>> remaining reference, i.e. when the reference count is one.
>
> Yes, but this is not Java's repsonsibility, right? If something else for any
> reason wants a reference to the same object, it can have it. As long as Java
> has relesed the object ASAP when not required any more, it is good enough I
> think.

The scenario I'm thinking of is Java creates two ObjC objects (call
them objA and objB) and also creates two Java wrapper objects (call
them javaA and javaB, respectively). If at some point objA is given a
reference to objB (e.g. one is an NSTextContainer and the other is an
NSTextView), and later javaB becomes unreachable and is finalized
while javaA remains reachable, the result is that javaA, objA, and
objB remain in memory and will continue to do so at least until javaA
becomes unreachable. Even though objB can no longer be reached
directly from a Java object, it's still Java's "responsibility" since
the only remaining reference chain may be via a Java object
(javaA->objA->objB).

> Technically it is possible to monitor (and partially replace) any method,
> even those who live on a library object, with method swizzling, but I don't
> feel it's necessary.

You're right, that's not necessary, and I don't recommend it. However
if you want to simultaneously minimize native memory usage and
minimize GC overhead, you'll need to have some idea how much memory is
being used to make an intelligent decision about whether to call
System.gc(). This might be useful:

http://stackoverflow.com/questions/787160/programmatically-retrieve-memory-usage-on-iphone

Terasgr

unread,
Oct 1, 2016, 9:29:08 AM10/1/16
to Avian


Τη Σάββατο, 1 Οκτωβρίου 2016 - 1:31:37 π.μ. UTC+3, ο χρήστης Joel Dice έγραψε:
On Thu, Sep 29, 2016 at 7:43 PM, Terasgr <tera...@gmail.com> wrote:
> Yes, but this is not Java's repsonsibility, right? If something else for any
> reason wants a reference to the same object, it can have it. As long as Java
> has relesed the object ASAP when not required any more, it is good enough I
> think.

The scenario I'm thinking of is Java creates two ObjC objects (call
them objA and objB) and also creates two Java wrapper objects (call
them javaA and javaB, respectively).  If at some point objA is given a
reference to objB (e.g. one is an NSTextContainer and the other is an
NSTextView), and later javaB becomes unreachable and is finalized
while javaA remains reachable, the result is that javaA, objA, and
objB remain in memory and will continue to do so at least until javaA
becomes unreachable.  Even though objB can no longer be reached
directly from a Java object, it's still Java's "responsibility" since
the only remaining reference chain may be via a Java object
(javaA->objA->objB).


This is a good scenario to discuss.

First of all still I don't believe that now it is Java's responsibility.
Java retained the objB once, and released it once. It did its duty. If, for any reason imaginable, objA needs a strong reference to objB, it can have it (as well as the responsibility to release it). As you pointed out, when javaA will be released, objA will be released and, as it is objA responsibility now, objA will release objA. So practically no memory leak here.


But ... there is a problem here that I didn't think about it in the first place.
Imagine that javaB has also some java instance variables, thus the connection objB->javaB is not trivial.
If javaA has a method through objA to retrieve objB, then there is no way to propery return a correct javaB object - except if javaB didn't die at all in the first place.

I think the only way to be safe is not to let the Java object die immediately. Instead using finalize (or even resurrecting PhantomReference object but this looks more of a hack instead of a feature) to resurrect the dying object and store it in a special zombie Collection. Of course adding to this collection means that the release on the objB will indeed be performed.
To remove objects from this collection, dealloc needs to be overriden for all objects, to notify Java that the object is indeed dead, and remove it from the zombie collection. If in the meantime a call objA.getObjB() is performed, instead of wrapping a new Java object to the pending objB object, first check the zombie collection if a java object is there and return this instead.

Although the problem is only with extended objects and not directly mapped objects, I think this mechanism should always be triggered, since as long as an ObjC object is still in memory (and Java requested it somehow), the thin Java wrapper should stil be around.

 

Thank you for the link!
 

 

Joel Dice .

unread,
Oct 1, 2016, 10:23:12 AM10/1/16
to Avian
On Sat, Oct 1, 2016 at 7:29 AM, Terasgr <tera...@gmail.com> wrote:
> First of all still I don't believe that now it is Java's responsibility.
> Java retained the objB once, and released it once. It did its duty. If, for
> any reason imaginable, objA needs a strong reference to objB, it can have it
> (as well as the responsibility to release it). As you pointed out, when
> javaA will be released, objA will be released and, as it is objA
> responsibility now, objA will release objA. So practically no memory leak
> here.

Right; I'm not concerned about memory leaks. I'm only concerned that
the peak memory use of the app may grow without bound if GC is
triggered only in response to the amount of memory occupied by Java
objects rather than the combined memory occupied by Java and ObjC
objects. In the extreme case, if the Java heap grows very slowly
while the ObjC heap grows quickly, GC won't happen frequently enough
to prevent the ObjC heap from becoming huge. You're right, though, as
soon as GC *does* happen, all the unreachable objects will be cleaned
up (assuming no reference count cycles).

> But ... there is a problem here that I didn't think about it in the first
> place.
> Imagine that javaB has also some java instance variables, thus the
> connection objB->javaB is not trivial.
> If javaA has a method through objA to retrieve objB, then there is no way to
> propery return a correct javaB object - except if javaB didn't die at all in
> the first place.
>
> I think the only way to be safe is not to let the Java object die
> immediately. Instead using finalize (or even resurrecting PhantomReference
> object but this looks more of a hack instead of a feature) to resurrect the
> dying object and store it in a special zombie Collection. Of course adding
> to this collection means that the release on the objB will indeed be
> performed.
> To remove objects from this collection, dealloc needs to be overriden for
> all objects, to notify Java that the object is indeed dead, and remove it
> from the zombie collection. If in the meantime a call objA.getObjB() is
> performed, instead of wrapping a new Java object to the pending objB object,
> first check the zombie collection if a java object is there and return this
> instead.
>
> Although the problem is only with extended objects and not directly mapped
> objects, I think this mechanism should always be triggered, since as long as
> an ObjC object is still in memory (and Java requested it somehow), the thin
> Java wrapper should stil be around.

It depends on how much state is in the Java wrapper. Assuming the
Java wrapper has only one field: the long pointer to the ObjC peer,
it's easier just to create a new one when you need it rather than try
to keep track of the old one. And if the old one happens to still
exist somewhere, it's no problem having more than one Java wrapper for
a single ObjC object. As long as all the wrappers increment and
decrement the reference count at the appropriate times (i.e. when
created and when they've become unreachable), the ObjC object will
still be deallocated when it finally becomes unreachable.

Terasgr

unread,
Oct 1, 2016, 10:48:43 AM10/1/16
to Avian


Τη Σάββατο, 1 Οκτωβρίου 2016 - 5:23:12 μ.μ. UTC+3, ο χρήστης Joel Dice έγραψε:

Right; I'm not concerned about memory leaks.  I'm only concerned that
the peak memory use of the app may grow without bound if GC is
triggered only in response to the amount of memory occupied by Java
objects rather than the combined memory occupied by Java and ObjC
objects.  In the extreme case, if the Java heap grows very slowly
while the ObjC heap grows quickly, GC won't happen frequently enough
to prevent the ObjC heap from becoming huge.  You're right, though, as
soon as GC *does* happen, all the unreachable objects will be cleaned
up (assuming no reference count cycles).


Yes, you are right here. Keeping an eye on the total memory consumption will be desirable. Like the strategy you mentioned; store the corrent memory footprint, keep an eye on it, if it increases "dramatically" perform a GC, store the new memory consumption, and repeat the whole cycle.
 



I am not talking about objects only with a peer pointer. These are the objects I meant before with "directly mapped objects". This is directly supported by extending PhantomReferences as you know.

I am talking more of objects that are extended and have some extra properties.
Take for example a class which extends UIView, like the one in hello-ios project, which needs extra variables/properties to properly draw. Probably not even pure Java objects but also other NSObjects as well.
If this object is removed from memory then all the state is lost.
If I still have a reference of its parent and perform something like "subviews().get(0)" to retrieve it, there is no safe way to actually do this.

Joel Dice .

unread,
Oct 2, 2016, 7:39:24 PM10/2/16
to Avian
On Sat, Oct 1, 2016 at 8:48 AM, Terasgr <tera...@gmail.com> wrote:
> I am not talking about objects only with a peer pointer. These are the
> objects I meant before with "directly mapped objects". This is directly
> supported by extending PhantomReferences as you know.
>
> I am talking more of objects that are extended and have some extra
> properties.
> Take for example a class which extends UIView, like the one in hello-ios
> project, which needs extra variables/properties to properly draw. Probably
> not even pure Java objects but also other NSObjects as well.
> If this object is removed from memory then all the state is lost.
> If I still have a reference of its parent and perform something like
> "subviews().get(0)" to retrieve it, there is no safe way to actually do
> this.

The simplest option might be to add those extra properties to the
Objective-C objects instead of adding them to the Java wrappers. I'm
not sure exactly what that would look like, though, since my
Objective-C skills are pretty rusty.
Reply all
Reply to author
Forward
0 new messages