Re: How to correctly populate CvSeq for cvDrawContours

706 views
Skip to first unread message

viliam...@gmail.com

unread,
Apr 12, 2013, 5:30:34 AM4/12/13
to jav...@googlegroups.com
I did further research. It only fails if I don't run the JVM in the debug mode, so I'm not able to reproduce the problem while debugging.

So I added some console output, and found out, that CvMemStorage really gets deallocated during GC. Actually I printed call stack inside the CvMemStorage.ReleaseDeallocator#deallocate() method:

    at com.googlecode.javacv.cpp.opencv_core$CvMemStorage$ReleaseDeallocator.deallocate(opencv_core.java:2863)
    at com.googlecode.javacpp.Pointer$DeallocatorReference.clear(Pointer.java:193)
    at com.googlecode.javacpp.Pointer.deallocateReferences(Pointer.java:202)
    at com.googlecode.javacpp.Pointer.deallocator(Pointer.java:253)
    at com.googlecode.javacpp.Pointer.init(Pointer.java:78)
    at com.googlecode.javacv.cpp.opencv_core$CvPoint.allocate(Native Method)
    at com.googlecode.javacv.cpp.opencv_core$CvPoint.<init>(opencv_core.java:2100)
    at com.googlecode.javacv.cpp.opencv_core.cvPoint(opencv_core.java:2203)
    at sk.kios.cvtest.Test.main(Test.java:21)

This happens just at next cvPoint creation after GC (I used System.gc() to simulate). I checked that it is the same Java instance as I created on the second line of my snippet.

I suspected the JVM bug, because PhantomReference should not put the reference to referenceQueue before the object is GC-ed. I tested with Oracle JDK 1.6.0_31, 1.6.0_41 (latest), 1.7.0_02, 1.7.0_17 (latest), all 64-bit, with no success.

I just noticed that a new of javacv and javacpp is out, will give it a try.

I even found a workaround: don't use CvMemStorage.create(), but rather cvCreateMemStorage() along with cvReleaseMemStorage()...

Viliam

Dňa piatok, 12. apríla 2013 8:44:31 UTC+2 viliam...@gmail.com napísal(-a):

I try to populate CvSeq as input for cvDrawContours, but the JVM almost reliably crashes with EXCEPTION_ACCESS_VIOLATION at various places, most often at [msvcr100.dll+0x3c19b] memcpy+0x20b or [opencv_core243.dll+0x61793] cvSeqPush+0x133


public
static void main(String[] args) { CvMemStorage memStorage = CvMemStorage.create(); CvSeq seq = cvCreateSeq(0, Loader.sizeof(CvSeq.class), Loader.sizeof(CvPoint.class), memStorage); for (int j=0; j<1000000; j++) { CvPoint cvPoint = cvPoint(j, j+1); System.out.println(j); cvSeqPush(seq, cvPoint); } }


In my configuration it fails most often after about 50000 iterations (with -Xmx64m -Xms64m, Oracle JVM 1.6.0_31 64 bit on Windows), but sometimes at other count or not at all. There is apparently some allocation/deallocation error.

As I found out, it fails after the heap is full and GC needs to be run. If I explicitly call System.gc() after 20000 iterations, it fails inside cvSeqPush right after the GC (or 1-2 iterations later, probably because pointer from deallocated space happens to point to correct address). Or if I decrease or increase heap size, it fails sooner or later. Probably something in use is automagically deallocated.

Is someone able to decipher the 0x61793 address from the opencv_core243.dll? This may give a clue.

Thank you in advance,

Viliam

Samuel Audet

unread,
Apr 14, 2013, 7:20:28 AM4/14/13
to jav...@googlegroups.com
Hello,

On 04/12/2013 06:30 PM, viliam...@gmail.com wrote:
> I suspected the JVM bug, because PhantomReference should not put the
> reference to referenceQueue before the object is GC-ed. I tested with
> Oracle JDK 1.6.0_31, 1.6.0_41 (latest), 1.7.0_02, 1.7.0_17 (latest), all
> 64-bit, with no success.

It's not really a bug. It's just Java being a bit smarter (ass) than we
wished it would be in this case. It figures that we don't use memStorage
for the rest of the method, because there are no more live Java
references, and decides to garbage collect it early before the method
returns, that's all. If we put something like
System.out.println(memStorage) after the loop or move the allocation
outside of the method, all's good.

> I try to populate CvSeq as input for cvDrawContours, but the JVM

Any reason to not use cvLine() or even cvPolyLine() with arrays?

Samuel

Viliam Ďurina

unread,
Apr 15, 2013, 3:54:08 AM4/15/13
to jav...@googlegroups.com
Ough, I did not think of this... Now it is clear and it works. This is
a case of any pointer, that it might get deallocated at any time after
its reference is not used...

I'm actually quite unsure about how all the allocation, deallocation
and native calls work. Consider this code:

cvSeqPush(someSeq, new CvPoint().x(x).y(y));

As far as I understand now, new Java instance of CvPoint is created,
along with two integers allocated on the native heap. Then two fields
are set on native field. Then the pointer to the CvPoint native object
is passed to native method. Native method copies the CvPoint data
(memcpy in C source). After the call, CvPoint Java instance is subject
to GC. When deallocating CvPoint, it's native data are freed too, but
thanks to copying inside of the cvSeqPush, the CvPoint in the CvSeq
remains live. Probably, deallocation of CvSeq deallocates all inserted
elements too.

Please correct me if I'm not correct. Understanding this is crucial,
because these error are hard to find (or even hard to reproduce).

The second thing I do not understand is how CvSeq itself is
deallocated. If it is created from Java it is deallocated at GC time,
but what if it is created from C code, for example with cvCreateSeq
(or as a result from, say, cvHoughLines2). I did a quick test and it
seems it _is_ deallocated, but don't know how:

public static void main(String[] args) {
for (int i=0; i<10000000; i++) {
CvMemStorage storage = cvCreateMemStorage(0);
CvSeq seq = cvCreateSeq(0, Loader.sizeof(CvSeq.class), 10, storage);
cvReleaseMemStorage(storage);
}
}

While running this the process memory did not grow up. I read
somwhere, that if I create the object with CvSomeClass.create(), it is
deallocated automatically, but if I create with C code (say with
cvCreateMat) i should release with cvReleaseMat(). Is this true? (By
the way, if I create the MemStorage with CvMemStorage.create(), it is
lot slower and uses lot more memory, but seems to work).

> Any reason to not use cvLine() or even cvPolyLine() with arrays?

No, I just did not find these. But let's take this case in general.

Thanks,
Viliam



2013/4/14 Samuel Audet <samuel...@gmail.com>:
> --
>
> --- You received this message because you are subscribed to a topic in the
> Google Groups "javacv" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/javacv/ffQSkfXnT0o/unsubscribe?hl=en.
> To unsubscribe from this group and all its topics, send an email to
> javacv+un...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Samuel Audet

unread,
Apr 20, 2013, 9:34:17 PM4/20/13
to jav...@googlegroups.com
On 04/15/2013 04:54 PM, Viliam Ďurina wrote:
> Ough, I did not think of this... Now it is clear and it works. This is
> a case of any pointer, that it might get deallocated at any time after
> its reference is not used...
>
> I'm actually quite unsure about how all the allocation, deallocation
> and native calls work. Consider this code:
>
> cvSeqPush(someSeq, new CvPoint().x(x).y(y));
>
> As far as I understand now, new Java instance of CvPoint is created,
> along with two integers allocated on the native heap. Then two fields
> are set on native field. Then the pointer to the CvPoint native object
> is passed to native method. Native method copies the CvPoint data
> (memcpy in C source). After the call, CvPoint Java instance is subject
> to GC. When deallocating CvPoint, it's native data are freed too, but
> thanks to copying inside of the cvSeqPush, the CvPoint in the CvSeq
> remains live. Probably, deallocation of CvSeq deallocates all inserted
> elements too.
>
> Please correct me if I'm not correct. Understanding this is crucial,
> because these error are hard to find (or even hard to reproduce).

Yes, that's correct

> The second thing I do not understand is how CvSeq itself is
> deallocated. If it is created from Java it is deallocated at GC time,
> but what if it is created from C code, for example with cvCreateSeq
> (or as a result from, say, cvHoughLines2). I did a quick test and it
> seems it _is_ deallocated, but don't know how:
>
> public static void main(String[] args) {
> for (int i=0; i<10000000; i++) {
> CvMemStorage storage = cvCreateMemStorage(0);
> CvSeq seq = cvCreateSeq(0, Loader.sizeof(CvSeq.class), 10, storage);
> cvReleaseMemStorage(storage);
> }
> }
>
> While running this the process memory did not grow up. I read
> somwhere, that if I create the object with CvSomeClass.create(), it is
> deallocated automatically, but if I create with C code (say with
> cvCreateMat) i should release with cvReleaseMat(). Is this true? (By
> the way, if I create the MemStorage with CvMemStorage.create(), it is
> lot slower and uses lot more memory, but seems to work).

CvSeq is a bit weird.. It's one of the things I don't like from the
original C API of OpenCV, but the new C++ API is overall worse, so
whatever. Hopefully someday something better will happen :)

Anyway, CvSeq uses CvMemStorage to allocate memory, it doesn't allocate
memory by itself. When storage gets freed, the data "stored inside" the
CvSeq becomes invalid. There is no API to free a CvSeq because it never
allocates memory.

Samuel
Reply all
Reply to author
Forward
0 new messages