thread safety

17 messages Options Options
Embed this Post
Permalink
richf

thread safety

Reply Threaded MoreMore options
Print post
Permalink
To what extent is mapserver currently thread safe?

Yes, I have read the page at:
  http://mapserver.gis.umn.edu/docs/faq/thread_safety
but it says:
  This FAQ applies to: MapServer 4.6, MapServer 4.8
and I'm wondering about 5.0.  (And there are some other things on it that I
don't understand as well, see details below.)

Here's my situation:

I am running mapserver 5.0.0 on debian 3.1 (sarge), locally compiled with gcc
3.3.5, using locally compiled versions of GD 2.0.35 and AGG 2.5, and debian
packages for everything else.  My mapserver configuration is as follows:

MapServer version 5.0.0 OUTPUT=GIF OUTPUT=PNG OUTPUT=WBMP OUTPUT=SVG SUPPORTS=PROJ SUPPORTS=AGG SUPPORTS=FREETYPE SUPPORTS=THREADS INPUT=POSTGIS INPUT=SHAPEFILE

I would imagine that the portions above that I am currently using are:
OUTPUT=PNG SUPPORTS=PROJ SUPPORTS=AGG SUPPORTS=FREETYPE SUPPORTS=THREADS INPUT=POSTGIS

I have a custom Java webapp that uses Java mapscript and runs with tomcat
5.5.17 and java 1.5.0, also on debian 3.1 (sarge).  It queries its data from a
PostgreSQL/PostGIS database, uses projections, uses the AGG renderer, includes
labels with TrueType fonts, and generates PNG output.  Each flow from request
to response is a single thread, but tomcat is able to process multiple
concurrent requests if it receives them.

Small scale individual tests work fine.  When I try executing larger
scale stress tests that include multiple concurrent requests to tomcat, things
run fine for a while, but eventually the JVM crashes and tomcat dies.  Some
examples of reported stack traces are given below.  I have not yet been able to
reproduce a single failure -- I'm still working on it.  But if this is a
threading issue, I may not be able to get a reproducible test case.

And even if the list at the web page above is still current, it's not entirely
clear to me as someone not familiar with mapserver internals how I can tell
whether or not some of those components apply to my particular situation.

There's this thread on the mailing list:

  http://www.nabble.com/forum/ViewPost.jtp?post=3215944&framed=y

which does say:

  "The --with-threads switch does not need to be used in normal cgi or single
  threaded mapscript use."

although that mailing list thread doesn't really answer all of my questions,
because:

1) Much of it discusses FastCGI, which isn't applicable to my situation.

2) It's not clear to me whether a single thread of execution from the
   perspective of mapscript, but that can be invoked repeatedly from tomcat
   handling multiple concurrent connections (from either a multithreaded
   client or multiple single threaded clients) falls under the case of "single
   threaded mapscript use.

3) The mailing list thread is nearly 2 years old.

Thanks for any help that anyone may be able to provide.

- Rich

---
crash #1:
(msClipPolygonRect) mapprimitive.c:649
(msDrawShape) mapdraw.c:1609
(msDrawVectorLayer) mapdraw.c:887
(msDrawLayer) mapdraw.c:718
(msDrawMap) mapdraw.c:432
(mapObj_draw) mapscript/java/mapscript_wrap.c:1714
(Java_edu_umn_gis_mapscript_mapscriptJNI_mapObj_1draw) mapscript/java/mapscript_wrap.c:20612

crash #2:
(msClipPolygonRect) mapprimitive.c:605
(msDrawShape) mapdraw.c:1609
(msDrawVectorLayer) mapdraw.c:887
(msDrawLayer) mapdraw.c:718
(msDrawMap) mapdraw.c:432
(mapObj_draw) mapscript/java/mapscript_wrap.c:1714
(Java_edu_umn_gis_mapscript_mapscriptJNI_mapObj_1draw) mapscript/java/mapscript_wrap.c:20612

crash #3:
(msFreeLabelCacheSlot) mapfile.c:4068
(msFreeLabelCache) mapfile.c:4100
(msFreeMap) mapobject.c:98
(delete_mapObj) mapscript/java/mapscript_wrap.c:1619
(Java_edu_umn_gis_mapscript_mapscriptJNI_delete_1mapObj) mapscript/java/mapscript_wrap.c:19695

crash #4:
(msFreeLabelCacheSlot) mapfile.c:4068
(msFreeLabelCache) mapfile.c:4100
(msFreeMap) mapobject.c:98
(delete_mapObj) mapscript/java/mapscript_wrap.c:1619
(Java_edu_umn_gis_mapscript_mapscriptJNI_delete_1mapObj) mapscript/java/mapscript_wrap.c:19695
richf

Re: thread safety

Reply Threaded MoreMore options
Print post
Permalink
rich.fromm wrote:
Small scale individual tests work fine.  When I try executing larger
scale stress tests that include multiple concurrent requests to tomcat, things
run fine for a while, but eventually the JVM crashes and tomcat dies.
<...>
I have not yet been able to
reproduce a single failure -- I'm still working on it.  But if this is a
threading issue, I may not be able to get a reproducible test case.
I was on the verge of completely blaming threading, but now I'm not so sure.
I have had two more crashes, see #5 and #6 below.  (#1 through #4 are in the
original mail.)  #5 looks to be identical to #3 and #4.  And all of #1 through
#5 occurred when tomcat was potentially handling multiple concurrent
requests.  In no case have I been able to reproduce the exact failure, even
when playing back requests that I think were the trigger of the failure.
Which seems to point to a threading problem.

But in crash #6, I had the client waiting until a request returned before
making another request.  So tomcat was invoking mapserver purely sequentially,
and I would have thought that threading problems were not an issue.
Nevertheless, eventually the server crashed, and once again if I replay the
exact request that triggered the failure, I can not reproduce the crash.

So I fear that it's going to be very hard for me to create a reproduceable
test case for this, but inevitably if I throw enough requests at mapserver, it
always crashes and brings down tomcat, usually within about an hour or two.

- Rich

---
crash #5:

(msFreeLabelCacheSlot) mapfile.c:4068
(msFreeLabelCache) mapfile.c:4100
(msFreeMap) mapobject.c:98
(delete_mapObj) mapscript/java/mapscript_wrap.c:1619
(Java_edu_umn_gis_mapscript_mapscriptJNI_delete_1mapObj) mapscript/java/mapscript_wrap.c:19695

crash #6:

(msImageCreateWithPaletteGD) mapgd.c:3471
(msSaveImageBufferGD) mapgd.c:3697
(msSaveImageBufferAGG) mapagg.cpp:2248
(msSaveImageBuffer) maputil.c:688
(imageObj_getBytes) mapscript/java/mapscript_wrap.c:2486
(Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes) mapscript/java/mapscript_wrap.c:24414
richf

Re: thread safety

Reply Threaded MoreMore options
Print post
Permalink
rich.fromm wrote:
inevitably if I throw enough requests at mapserver, it
always crashes and brings down tomcat, usually within about an hour or two.
After searching around the mailing list archives a bit more, I am beginning to
suspect that this may be caused by the JVM gc, and maybe improper use of
delete() within mapscript:

  http://www.nabble.com/Re%3A--UMN_MAPSERVER-USERS---mapserver--Java-VM-Crashes-using-the-Mapscript-API-to1548675.html#a1548675
  http://www.nabble.com/mapscript-jvm-crash-on-removeLayer-to6206628.html#a6206628

From reading through those threads, it's still not clear to me under precisely
what circumstances it is necessary to call delete() on a java mapscript
object, and under what circumstances it is sufficient to let the garbage
collector handle it.  But examing my code further, I do appear to be
inconsistent.  Here is a simplified version of pseudo-code showing my object
creations and deletions:

--- begin ---
  mapObj map = new mapObj();

  pointObj pt1 = new pointObj();
  pointObj pt2 = new pointObj();
  rectObj rect = new rectObj();

  for (i ...) {
    layerObj layer = map.getLayer(i);
    for (j ...) {
      classObj class = layer.getClass(j);
      labelObj label = class.getLabel();
    }
  }

  imageObj img = map.draw();

  img.delete();
  map.delete();
--- end ---

Specifically I am NOT calling delete() on either of the pointObj's, on the
rectObj, on the layerObj in the outer loop, or on the classObj or labelObj in
the inner loop.

I am going to try inserting the maximum number of applicable delete()'s and
see if that helps.

- Rich
richf

leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
Note that subject has changed, previous mailing list thread is here:
http://www.nabble.com/thread-safety-to15514019.html

rich.fromm wrote:
I am going to try inserting the maximum number of applicable delete()'s and
see if that helps.
Indeed, I am leaking memory, and I think that that's likely the root of my
problems, and not threading issues.  (I'm currently having my client only
generate sequential requests.)

I am now explicitly calling delete() on every mapserver object that I
reference in any way when I'm done with it.  Yet the java process for tomcat
still grows in size, and eventually gets to its allowed max and crashes the
JVM and tomcat.  I'm not certain whether or not the rate of leaking has gone
down, I haven't collected detailed enough data to be able to determine that.

Other than calling delete() on every mapserver object as applicable, is there
anything else that I can be doing?  I fear that there may be a real genuine
leak within mapserver, and that that might be hard to track down.

- Rich
umn-ms

Antwort: Re: thread safety

Reply Threaded MoreMore options
Print post
Permalink
In reply to this post by richf

Hi Rich

I saw, that you made a thread on java/threading/memory-leaks "on your own".
Although I spend some time on the same issues in the last years I can hardly
give new input.

Don't know wether you saw these:
1. http://www.nabble.com/forum/ViewPost.jtp?post=5707320&framed=y
   We worked around threading-problems with Java-synchronizing.
   By doing so, the application becomes stable in stress-tests, but  
   looses the ability to use many CPUs.
   (On the other hand: http://www.nabble.com/forum/ViewPost.jtp?post=15410399&framed=y
    shows, that the mapserver-locks prevent using many CPU's in any case.)
2. You cited the post about the delete-problem.
   (http://www.nabble.com/forum/ViewPost.jtp?post=1548675&framed=y)
   This issue was adressed in Mapserver 5.0:
   http://mapserver.gis.umn.edu/development/rfc/ms-rfc-24
   So the garbage-collector shouldn't crash the jvm any more.
   (My personal opinion is to use delete anyway, since freeing C-memory
    in the garbage-collector-thread sounds dangerous to me. But as far
    as I know, there are no more documented problems.)
3. I also spent time in watching memory-leaks inside Mapscript/java and abandoned
   valgrind-approach. Seemed to be to complex, since much memory-handling of the JVM is
   documented. My approaches:
   Variant 1: In cases, where I had a guess what to look for, I wrote a small C-program, which
     called the suspect functions and analysed the behaviour of this small program.
     (Successfully used in http://trac.osgeo.org/mapserver/ticket/1662.)
     Didn't use valgrind but memwatch (http://www.linkdata.se/sourcecode.html). But this
     is purely a matter of taste.
     Disadvantage: Doesn't help, if you have no guess.
   Variant 2: Watching memory-usage using top from outside during stress-tests. With
     some awk-sed-batch-unix-stuff it resulted in pictures like this:
     http://trac.osgeo.org/mapserver/attachment/ticket/1662/before-after.zip
     Disadvantage: Black box

Sorry. That's hardly helpfull. But I'm interested in your progress.
Benedikt Rothe

mapserver-users-bounces@... schrieb am 20.02.2008 02:26:04:

>
> Any advice about tracking down memory leaks that might exist in java
> mapscript?
>
> For the context of why I'm trying to do this, see the following thread:
>
>   http://www.nabble.com/thread-safety-to15514019.html
>
> I installed the debian sarge backport of valgrind (valgrind-3.2.0-Debian),
> and
> tried invoking java prefaced with:
>
>   valgrind --leak-check=full --smc-check=all -v
>
> No leaks were reported, but I don't think it really checked where I wanted
> it
> to check.  libmapscript.so is loaded with the following:
>
>          System.loadLibrary("mapscript");
>
> And from the output it doesn't look like valgrind is actually instrumenting
> that library, and I can't figure out how to tell it to explicitly instrument
> a
> dynamically loaded library.
>
> Yet the valgrind FAQ:
>
>   http://valgrind.org/docs/manual/faq.html#faq.java
>
> does indicate that this might at least be possible:
>
> --- begin ---
> in theory Valgrind can run any Java program just fine, even those that use
> JNI and are partially implemented in other languages like C and C++.  In
> practice, Java implementations tend to do nasty things that most programs
> do not, and Valgrind sometimes falls over these corner cases.
> --- end ---
>
> Sorry if this is more appropriately a valgrind question and not a mapserver
> question, but I saw valgrind mentioned in a few places:
>
>   HISTORY.TXT
>   http://trac.osgeo.org/mapserver/ticket/2412
>
> so I thought that maybe someone here might have experience as it relates to
> this specific case.
>
> - Rich
>
> --
> View this message in context: http://www.nabble.com/detecting-
> memory-leaks-in-java-mapscript-tp15580371p15580371.html
> Sent from the Mapserver - User mailing list archive at Nabble.com.
>
> _______________________________________________
> mapserver-users mailing list
> mapserver-users@...
> http://lists.osgeo.org/mailman/listinfo/mapserver-users

_______________________________________________
mapserver-users mailing list
mapserver-users@...
http://lists.osgeo.org/mailman/listinfo/mapserver-users
richf

leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
Benedikt Rothe wrote:
Sorry. That's hardly helpfull. But I'm interested in your progress.
Thanks for your input.  It's not "hardly helpful".  But I will give a few
clarifications:

1) I think my threading worries may have been a red herring.  When I started
   experiencing JVM crashes during stress tests that were not reproducible, my
   first conclusion was a threading problem.  But further investigation points
   to memory leaks, esp. since I can crash the JVM even with a sequential test
   client.  I can watch the memory consumption with top, and eventually it
   runs out of memory and crashes the JVM and tomcat dies.  I can somewhat
   ironically cause the crash to happen faster by allocating more memory to
   tomcat, since that leaves less memory available for the JNI code.

2) I don't think it's the gc that's crashing the JVM.  It's just that the JNI
   code is out of memory, so some malloc is going to fail.  But it doesn't
   cleanly report that back to Java as an OutOfMemoryError, the malloc just
   returns a null.  So something that ought to be pointing to valid memory in
   the JNI code is now pointing to null, and when that pointer is
   dereferenced, it causes a segfault, which crashes the JVM.

3) As far as I can tell, I am calling delete() for every mapserver object that
   I reference as soon as I am done with it.  Yet the code is still leaking
   memory, leading me to suspect an actual memory leak internal to mapserver
   (or the mapscript interface).

4) Valgrind seems very useful for tracking down the problem in (3), if I can
   get it to work.  In theory (based on the reading the FAQ and the valgrind
   mailing list), I think I ought to be able to do this.  But I think my
   problem is that it's not even instrumenting libmapscript.so, and I can't
   figure out why.  For more details:

   http://www.nabble.com/memcheck-of-JNI-library-loaded-via-System.loadLibrary%28%29-to15582655.html

So my approach right now is to try and get valgrind to work.  If that doesn't
work, another tool that was suggested to me (besides memwatch) is memprof:

   http://www.gnome.org/projects/memprof/

And taking java out of the loop might simplify things, but I'm not sure how
easy or hard that might be.  There is no C or C++ mapscript, but I suppose if
I take a look at what mapscript is really doing under the covers, I could
write a pure C or C++ program that links in libmapscript.so.

Only after I solve the memory problem will I take a look at multithreaded
issues.

- Rich
richf

Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
In reply to this post by richf
rich.fromm wrote:
Other than calling delete() on every mapserver object as applicable, is there
anything else that I can be doing?  I fear that there may be a real genuine
leak within mapserver, and that that might be hard to track down.
I have narrowed this down to a smaller testcase, that only has two mapscript
objects.

I have a fixed map file, and I'm doing barely anything, just creating the map
object from the map file, drawing to the image object, and getting the bytes.
I am calling both map.delete() and img.delete() at the end.  Then I loop over
all of this repeatedly.

This is a little bit of a simplification, but essentially what I'm doing is
this:

      for (int i = 0; i < loop; i++) {
         mapObj map = null;
         imageObj img = null;

         try {
            map = new mapObj(filename);
            img = map.draw();
            byte[] bytes = img.getBytes();
         } catch (Exception e) {
            System.err.println("Caught exception: " + e);
            e.printStackTrace(System.err);
         } finally {
            if (img != null) {
               img.delete();
            }
            if (map != null) {
               map.delete();
            }
         }
         System.gc();
      } // end for

I have tried a number of variants of this (including outputting the bytes to a
file), and the offending line seems to be the call to imageObj.getBytes().
With it, my program leaks.  Without it, it does not:

[rich@peyote mapserver_test 17:53:31]$ <mapserver_test> diff -u java/src/TestMapscript.java.bak1 java/src/TestMapscript.java.bak3
--- java/src/TestMapscript.java.bak1 2008-02-20 17:37:43.000000000 -0800
+++ java/src/TestMapscript.java.bak3 2008-02-20 17:53:27.000000000 -0800
@@ -1,4 +1,4 @@
-// this code does NOT leak
+// this code DOES leak
 
 import edu.umn.gis.mapscript.mapObj;
 import edu.umn.gis.mapscript.imageObj;
@@ -47,7 +47,7 @@
             System.err.println("Drawing map to image object");
             img = map.draw();
 
-//             byte[] bytes = img.getBytes();
+            byte[] bytes = img.getBytes();
 //             int width = img.getWidth();
 //             int height = img.getHeight();
 //             System.err.println("The image is of size " + width + " x " + height + " and contains " + bytes.length + " bytes");
[rich@peyote mapserver_test 17:53:53]$ <mapserver_test>

I am calling System.gc() at the end of each iteration.  And by examining the
memory from java's perspective (Runtime.getRuntime().freeMemory() and
Runtime.getRuntime().totalMemory()) I can see that it is reasonably stable.
However, the memory as reported by top gradually grows.

The source for imageObj.getBytes() is as follows:

mapscript/java/edu/umn/gis/mapscript/imageObj.java

  public byte[] getBytes() {
    return mapscriptJNI.imageObj_getBytes(swigCPtr);
  }

I don't totally understand how all of the SWIG stuff works, but I can follow
it a bit.  The source for this is:

  getBytes() (mapscript/swiginc/image.i)

which notes the following:

    /*
    -------------------------------------------------------------------------
    getBytes returns a gdBuffer structure (defined in mapscript.i) which must
    be typemapped to an object appropriate to the target language.  This
    typemap must also gdFree the data member of the gdBuffer.  See the type-
    maps in java/javamodule.i and python/pymodule.i for examples.

    contributed by Jerry Pisk, jerry.pisk@gmail.com
    -------------------------------------------------------------------------
    */

And all of the following:

  msSaveImageBuffer() (maputil.c)
  msSaveImageBufferAGG() (mapagg.cpp)
  msSaveImageBufferGD() (mapgd.c)

confirm that the caller must free the returned array (with gdFree()).

What's not entirely clear to me is whether "the caller" means the java
mapscript code, or the application writer.  In other words, am I responsible
for doing this, or should this happen when I call img.delete() ?  Here is the
code for imageObj.delete():

mapscript/java/edu/umn/gis/mapscript/imageObj.java

  public void delete() {
    if(swigCPtr != 0 && swigCMemOwn) {
      swigCMemOwn = false;
      mapscriptJNI.delete_imageObj(swigCPtr);
    }
    swigCPtr = 0;
  }

But even though I don't see a call to gdFree() there, it's not clear to me
that it's not happening somewhere.  I don't understand the SWIG path enough,
and I haven't yet tried following it in gdb.  (Although that might be worth
trying.)

If I am responsible for doing this, how do I do it?  It appears to me that
gdFree() is not part of the mapscript API (it doesn't appear in the docs), and
just that it is just a mapscript internal function.

Right now my test case is using a fairly complicated mapfile from a large data
source located in a postgis database.  I will try to see if I can replicate
this problem with a simpler map file and/or synthetic data.

But in the mean time I wanted to post this in case anyone might have some
advice based on these observations.

Once again, thanks for any help that anyone might be able to provide.

- Rich

p.s. This is all based on mapserver 5.0.0
richf

Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
rich.fromm wrote:
p.s. This is all based on mapserver 5.0.0
I had built Java mapscript as shown below with the swig version for debian 3.1
(sarge), which is 1.3.24.  It seemed to work, so I had assumed that everything
was okay.

But I do see that mapscript/java/README says the following:

--- begin ---
SWIG wrappers
-------------

MapServer releases, beginning with 4.2.4, contain pre-generated wrapper code
(mapscript/java/mapscript_wrap.c) and class files (mapscript/java/edu).
Nevertheless it is recommended that you generate your own using the "interface"
target in the Java Makefile.

Requires swig > 1.3.24. Version 1.3.28 or 1.3.29 are recommended.
--- end ---

I guess I assumed that because 1.3.24 seemed to work for me that perhaps the
'>' above was really a type that should have been '>='.  But perhaps it really
is significant?  I'll try to rebuild tomorrow with a later version of swig and
see if it makes a difference.

--- begin ---
[rich@peyote mapserver-5.0.0-5 16:09:21]$ pwd
/ext/home/rich/wrk/mapserver/mapserver-5.0.0-5
[rich@peyote mapserver-5.0.0-5 16:09:22]$ cd mapscript/java
[rich@peyote java 16:09:25]$ export JAVA_HOME=/usr/lib/j2sdk1.5-sun
[rich@peyote java 16:09:31]$ make interface
mkdir -p edu/umn/gis/mapscript
swig -java -package edu.umn.gis.mapscript -outdir edu/umn/gis/mapscript -o mapscript_wrap.c ../mapscript.i
[rich@peyote java 16:09:39]$ make
gcc -fpic -c -g -O2 -fPIC -Wall  -DHAVE_VSNPRINTF -DNEED_STRLCAT   -DUSE_PROJ      -DUSE_GD_GIF -DUSE_GD_PNG -DUSE_GD_WBMP -DUSE_GD_FT -DGD_HAS_FTEX_XSHOW -DGD_HAS_GDIMAGEGIFPTR -DGD_HAS_GETBITMAPFONTS -DUSE_AGG      -DUSE_ICONV -DUSE_POSTGIS  -DUSE_THREAD -I/usr/local/include -I/usr/local/include/agg2 -I/usr/include/freetype2         -I/usr/include/postgresql        -I/usr/lib/j2sdk1.5-sun/include -I/usr/lib/j2sdk1.5-sun/include/linux -fno-strict-aliasing mapscript_wrap.c
gcc -fpic -shared mapscript_wrap.o -o libmapscript.so  -L../.. -lmapserver -L/usr/local/lib -lgd  -lfreetype -lpng -lz   -L/usr/local/lib -lagg -laggfontfreetype  -lfreetype -lpng -lz   -lproj     -L/usr/lib -lpq    -lpthread -lm -lstdc++    
javac edu/umn/gis/mapscript/*.java
jar cf mapscript.jar edu
[rich@peyote java 16:10:14]$
--- end ---
umn-ms

Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
In reply to this post by richf

Hi

> I don't totally understand how all of the SWIG stuff works, but I can follow
> it a bit.  The source for this is:
It's easier to watch the SWIG-generated JNI-Code in mapscript/java/mapscript_wrap.c

Example:
  mapscriptJNI.delete_imageObj(swigCPtr);
is implemented by the C-function
  SWIGEXPORT jbyteArray JNICALL Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes

> confirm that the caller must free the returned array (with gdFree()).
At the end of the long-named C-function you'll find a call to gdFree


Can you estimate how much memory is lost in each step of your
testprogram? Is it approx. the size of the image?

Benedikt Rothe


mapserver-users-bounces@... schrieb am 21.02.2008 03:31:09:

>
>
> rich.fromm wrote:
> >
> > Other than calling delete() on every mapserver object as applicable, is
> > there
> > anything else that I can be doing?  I fear that there may be a real
> > genuine
> > leak within mapserver, and that that might be hard to track down.
> >
>
> I have narrowed this down to a smaller testcase, that only has two mapscript
> objects.
>
> I have a fixed map file, and I'm doing barely anything, just creating the
> map
> object from the map file, drawing to the image object, and getting the
> bytes.
> I am calling both map.delete() and img.delete() at the end.  Then I loop
> over
> all of this repeatedly.
>
> This is a little bit of a simplification, but essentially what I'm doing is
> this:
>
>       for (int i = 0; i < loop; i++) {
>          mapObj map = null;
>          imageObj img = null;
>
>          try {
>             map = new mapObj(filename);
>             img = map.draw();
>             byte[] bytes = img.getBytes();
>          } catch (Exception e) {
>             System.err.println("Caught exception: " + e);
>             e.printStackTrace(System.err);
>          } finally {
>             if (img != null) {
>                img.delete();
>             }
>             if (map != null) {
>                map.delete();
>             }
>          }
>          System.gc();
>       } // end for
>
> I have tried a number of variants of this (including outputting the bytes to
> a
> file), and the offending line seems to be the call to imageObj.getBytes().
> With it, my program leaks.  Without it, it does not:
>
> [rich@peyote mapserver_test 17:53:31]$ <mapserver_test> diff -u
> java/src/TestMapscript.java.bak1 java/src/TestMapscript.java.bak3
> --- java/src/TestMapscript.java.bak1   2008-02-20 17:37:43.000000000 -0800
> +++ java/src/TestMapscript.java.bak3   2008-02-20 17:53:27.000000000 -0800
> @@ -1,4 +1,4 @@
> -// this code does NOT leak
> +// this code DOES leak
>  
>  import edu.umn.gis.mapscript.mapObj;
>  import edu.umn.gis.mapscript.imageObj;
> @@ -47,7 +47,7 @@
>              System.err.println("Drawing map to image object");
>              img = map.draw();
>  
> -//             byte[] bytes = img.getBytes();
> +            byte[] bytes = img.getBytes();
>  //             int width = img.getWidth();
>  //             int height = img.getHeight();
>  //             System.err.println("The image is of size " + width + " x " +
> height + " and contains " + bytes.length + " bytes");
> [rich@peyote mapserver_test 17:53:53]$ <mapserver_test>
>
> I am calling System.gc() at the end of each iteration.  And by examining the
> memory from java's perspective (Runtime.getRuntime().freeMemory() and
> Runtime.getRuntime().totalMemory()) I can see that it is reasonably stable.
> However, the memory as reported by top gradually grows.
>
> The source for imageObj.getBytes() is as follows:
>
> mapscript/java/edu/umn/gis/mapscript/imageObj.java
>
>   public byte[] getBytes() {
>     return mapscriptJNI.imageObj_getBytes(swigCPtr);
>   }
>
> I don't totally understand how all of the SWIG stuff works, but I can follow
> it a bit.  The source for this is:
>
>   getBytes() (mapscript/swiginc/image.i)
>
> which notes the following:
>
>     /*
>    
> -------------------------------------------------------------------------
>     getBytes returns a gdBuffer structure (defined in mapscript.i) which
> must
>     be typemapped to an object appropriate to the target language.  This
>     typemap must also gdFree the data member of the gdBuffer.  See the type-
>     maps in java/javamodule.i and python/pymodule.i for examples.
>
>     contributed by Jerry Pisk, jerry.pisk@...
>    
> -------------------------------------------------------------------------
>     */
>
> And all of the following:
>
>   msSaveImageBuffer() (maputil.c)
>   msSaveImageBufferAGG() (mapagg.cpp)
>   msSaveImageBufferGD() (mapgd.c)
>
> confirm that the caller must free the returned array (with gdFree()).
>
> What's not entirely clear to me is whether "the caller" means the java
> mapscript code, or the application writer.  In other words, am I responsible
> for doing this, or should this happen when I call img.delete() ?  Here is
> the
> code for imageObj.delete():
>
> mapscript/java/edu/umn/gis/mapscript/imageObj.java
>
>   public void delete() {
>     if(swigCPtr != 0 && swigCMemOwn) {
>       swigCMemOwn = false;
>       mapscriptJNI.delete_imageObj(swigCPtr);
>     }
>     swigCPtr = 0;
>   }
>
> But even though I don't see a call to gdFree() there, it's not clear to me
> that it's not happening somewhere.  I don't understand the SWIG path enough,
> and I haven't yet tried following it in gdb.  (Although that might be worth
> trying.)
>
> If I am responsible for doing this, how do I do it?  It appears to me that
> gdFree() is not part of the mapscript API (it doesn't appear in the docs),
> and
> just that it is just a mapscript internal function.
>
> Right now my test case is using a fairly complicated mapfile from a large
> data
> source located in a postgis database.  I will try to see if I can replicate
> this problem with a simpler map file and/or synthetic data.
>
> But in the mean time I wanted to post this in case anyone might have some
> advice based on these observations.
>
> Once again, thanks for any help that anyone might be able to provide.
>
> - Rich
>
> p.s. This is all based on mapserver 5.0.0
>
> --
> View this message in context: http://www.nabble.com/thread-safety-
> tp15514019p15603603.html
> Sent from the Mapserver - User mailing list archive at Nabble.com.
>
> _______________________________________________
> mapserver-users mailing list
> mapserver-users@...
> http://lists.osgeo.org/mailman/listinfo/mapserver-users

_______________________________________________
mapserver-users mailing list
mapserver-users@...
http://lists.osgeo.org/mailman/listinfo/mapserver-users
richf

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
Benedikt Rothe wrote:
It's easier to watch the SWIG-generated JNI-Code in
mapscript/java/mapscript_wrap.c
Example:
  mapscriptJNI.delete_imageObj(swigCPtr);
is implemented by the C-function
  SWIGEXPORT jbyteArray JNICALL
Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes

> confirm that the caller must free the returned array (with gdFree()).
At the end of the long-named C-function you'll find a call to gdFree

Can you estimate how much memory is lost in each step of your
testprogram? Is it approx. the size of the image?
I do see the gdFree() call.  Nevertheless, just to take the SWIG version issue
hopefully off of the table, I tried recompiling everything on debian etch
(4.0), which has a dpkg for swig 1.3.29.  I still get the leak.

Running my test for 100 iterations, it leaks approximately 30 MB.  Which is a
little bit more than 300 KB per iteration:

(/ (* 30 1024 1024) 100)
314572

Note that this figure is approximate, since the 30 MB is just from top.

The leak is independent of the image size.  I get the same amount of leaking
with a test image of 79813 bytes and one of 30328 bytes.  If I run 200
iterations instead of 100, the amount that is leaked doubles.

I have finally been able to successfully run a single iteration in valgrind
and have it report a leak in the code:

--- begin ---
==21154== 41,600 bytes in 65 blocks are possibly lost in loss record 46 of 55
==21154==    at 0x401C6CA: calloc (vg_replace_malloc.c:279)
==21154==    by 0x42B15E3: gdCalloc (gdhelpers.c:79)
==21154==    by 0x42A4F49: gdImageCreate (gd.c:104)
==21154==    by 0x40BFEA9: msImageCreateWithPaletteGD (mapgd.c:3460)
==21154==    by 0x40C04E8: msSaveImageBufferGD (mapgd.c:3695)
==21154==    by 0x40CCD6B: msSaveImageBufferAGG (mapagg.cpp:2248)
==21154==    by 0x40A70F8: msSaveImageBuffer (maputil.c:682)
==21154==    by 0x406CB6C: Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes (mapscript_wrap.c:2586)
==21154==    by 0x88614AA: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x8859218: ???
==21154==
==21154==
==21154== 271,588 (7,268 direct, 264,320 indirect) bytes in 1 blocks are definitely lost in loss record 49 of 55
==21154==    at 0x401D38B: malloc (vg_replace_malloc.c:149)
==21154==    by 0x42B15AC: gdMalloc (gdhelpers.c:85)
==21154==    by 0x42A4EA4: gdImageCreate (gd.c:83)
==21154==    by 0x40BFEA9: msImageCreateWithPaletteGD (mapgd.c:3460)
==21154==    by 0x40C04E8: msSaveImageBufferGD (mapgd.c:3695)
==21154==    by 0x40CCD6B: msSaveImageBufferAGG (mapagg.cpp:2248)
==21154==    by 0x40A70F8: msSaveImageBuffer (maputil.c:682)
==21154==    by 0x406CB6C: Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes (mapscript_wrap.c:2586)
==21154==    by 0x88614AA: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x8859218: ???
--- end ---

The amount reported matches up quite nicely with the amount that I'm
observing, so I'm inclined to believe the valgrind report:

(+ 271588 41600)
313188

While the memory is indeed being allocated outside of mapserver code (this is
with gd 2.0.35):

(gd.c:104)
      im->pixels[i] = (unsigned char *) gdCalloc (sx, sizeof (unsigned char));

(gd.c:83)
  im = (gdImage *) gdMalloc (sizeof (gdImage));

the function gdImageCreate() returns a gdImagePrt:

BGD_DECLARE(gdImagePtr) gdImageCreate (int sx, int sy)
{
  // ...
  gdImagePtr im;
  // ...
  return im;
}

to mapserver:

(mapgd.c:3460)
  img = gdImageCreate(sx, sy);

and I would assume that it is the responsibility of mapserver to free the
memory.  (Do you agree?)

Esp. since this happens to me for both test cases of different requests, I
suspect that I could create a synthetic test case independent of my postgis
data to create this, but I haven't yet done so.

I will investigate this further, I just wanted to post this update here.

- Rich
richf

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
rich.fromm wrote:
I have finally been able to successfully run a single iteration in valgrind
and have it report a leak in the code:

--- begin ---
==21154== 41,600 bytes in 65 blocks are possibly lost in loss record 46 of 55
==21154==    at 0x401C6CA: calloc (vg_replace_malloc.c:279)
==21154==    by 0x42B15E3: gdCalloc (gdhelpers.c:79)
==21154==    by 0x42A4F49: gdImageCreate (gd.c:104)
==21154==    by 0x40BFEA9: msImageCreateWithPaletteGD (mapgd.c:3460)
==21154==    by 0x40C04E8: msSaveImageBufferGD (mapgd.c:3695)
==21154==    by 0x40CCD6B: msSaveImageBufferAGG (mapagg.cpp:2248)
==21154==    by 0x40A70F8: msSaveImageBuffer (maputil.c:682)
==21154==    by 0x406CB6C: Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes (mapscript_wrap.c:2586)
==21154==    by 0x88614AA: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x8859218: ???
==21154==
==21154==
==21154== 271,588 (7,268 direct, 264,320 indirect) bytes in 1 blocks are definitely lost in loss record 49 of 55
==21154==    at 0x401D38B: malloc (vg_replace_malloc.c:149)
==21154==    by 0x42B15AC: gdMalloc (gdhelpers.c:85)
==21154==    by 0x42A4EA4: gdImageCreate (gd.c:83)
==21154==    by 0x40BFEA9: msImageCreateWithPaletteGD (mapgd.c:3460)
==21154==    by 0x40C04E8: msSaveImageBufferGD (mapgd.c:3695)
==21154==    by 0x40CCD6B: msSaveImageBufferAGG (mapagg.cpp:2248)
==21154==    by 0x40A70F8: msSaveImageBuffer (maputil.c:682)
==21154==    by 0x406CB6C: Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes (mapscript_wrap.c:2586)
==21154==    by 0x88614AA: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x885BA63: ???
==21154==    by 0x8859218: ???
--- end ---
Here's my hunch from a somewhat cursory inspection of the code.

This is around mapgd.c:3691:

unsigned char *msSaveImageBufferGD(gdImagePtr img, int *size_ptr, outputFormatObj *format)
{
  unsigned char *imgbytes;
  // ...
    if( force_palette ) {
      gdImagePtr gdPImg;
      const char *palette = msGetOutputFormatOption( format, "PALETTE", "palette.txt");

      gdPImg = msImageCreateWithPaletteGD(img, palette, gdImageSX(img), gdImageSY(img));

      msImageCopyForcePaletteGD(img, gdPImg);
      imgbytes = gdImagePngPtr(gdPImg, size_ptr);
    }
  // ...
  return imgbytes;
}

the (gdImagePtr gdPImg) is a pointer to the bytes created by the gd code.

But what is returned to mapscript (see the function below from around
mapscript_wrap.c:2581), is the (unsigned char *imgbytes) above.

SWIGINTERN gdBuffer imageObj_getBytes(imageObj *self){
        // ...
        buffer.data = msSaveImageBuffer(self, &buffer.size, self->format);
        // ...
        return buffer;
    }

so when the JNI wrapper calls gdFree() at mapscript_wrap.c:25804:

JNIEXPORT jbyteArray JNICALL Java_edu_umn_gis_mapscript_mapscriptJNI_imageObj_1getBytes(JNIEnv *jenv, jclass jcls, jlong jarg1) {
  jbyteArray jresult = 0 ;
  // ...
    result = imageObj_getBytes(arg1);
  // ..
  jresult = SWIG_JavaArrayOutSchar(jenv, (&result)->data, (&result)->size);
  if( (&result)->owns_data ) gdFree((&result)->data);
  return jresult;
}

it's only the (unsigned char *imgbytes) from above (mapgd.c) that gets freed,
leaking the original (gdImagePtr gdPImg) allocated by the gd code.

At least that's my guess, still exploring further.

Perhaps I should try to recreate this as a self contained test case, and then
I could file a bug.

- Rich
Tamas Szekeres

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
2008/2/21, rich.fromm <nospam420@...>:
>
>  it's only the (unsigned char *imgbytes) from above (mapgd.c) that gets
>  freed,
>  leaking the original (gdImagePtr gdPImg) allocated by the gd code.
>
>  At least that's my guess, still exploring further.
>

Rich,

Since gdImagePtr is a struct and allocated on the stack, it will be
freed when it goes out of the actual scope.

>  Perhaps I should try to recreate this as a self contained test case, and
>  then
>  I could file a bug.
>

I think that would be pretty useful.


BTW: I don't really understand your valgrind output. Maybe you have 2
different versions of the gd library out there side by side. The
mapserver core and the mapscript wrapper may use different gd (or crt)
libraries therefore using gdFree doesn't actually free the memory
segment passed by the argument. (I guess)


Best regards,

Tamas
_______________________________________________
mapserver-users mailing list
mapserver-users@...
http://lists.osgeo.org/mailman/listinfo/mapserver-users
richf

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
Tamas Szekeres wrote:
2008/2/21, rich.fromm <nospam420@yahoo.com>:
>
>  it's only the (unsigned char *imgbytes) from above (mapgd.c) that gets
>  freed,
>  leaking the original (gdImagePtr gdPImg) allocated by the gd code.
>
>  At least that's my guess, still exploring further.
>

Since gdImagePtr is a struct and allocated on the stack, it will be
freed when it goes out of the actual scope.
The actual memory allocation happens in gdImageCreate() in gd.c, which returns
gdImagePtr im.

This sets gdImagePtr img in msImageCreateWithPaletteGD() in mapgd.c, which
returns it.

This then sets gdImagePtr gdPImg in msSaveImageBufferGD() in mapgd.c.  This
value is NOT directly returned.  Instead, it is used to set unsigned char
*imgbytes, and that is returned.

The fact that all of these:

  gdImagePtr im      in  gdImageCreate()               in  gd.c
  gdImagePtr img     in  msImageCreateWithPaletteGD()  in  mapgd.c
  gdImagePtr gdPImg  in  msSaveImageBufferGD()         in  mapgd.c

are all local to the stacks of their respective functions is I think
irrelevant.  A gdImagePtr is a pointer.  From gd.h:

  typedef gdImage *gdImagePtr;

If the pointer goes out of scope and the memory to which it points has not
been freed, that's a memory leak.

Tamas Szekeres wrote:
>  Perhaps I should try to recreate this as a self contained test case, and
>  then
>  I could file a bug.
>

I think that would be pretty useful.
I have been able to replicate the leak with synthetic data.  Valgrind reports
it, and by iterating repeatedly in my test code I can easily see it as well.

I am in the process of trying to pare down the case to see what does or does
not matter for the leak (e.g. forced pallette, postgis vs. shp file, gd
vs. agg).  Once I am done with this I will package it up to file as a bug
report.

Tamas Szekeres wrote:
BTW: I don't really understand your valgrind output. Maybe you have 2
different versions of the gd library out there side by side. The
mapserver core and the mapscript wrapper may use different gd (or crt)
libraries therefore using gdFree doesn't actually free the memory
segment passed by the argument. (I guess)
(mapgd.c:3460) calls gdImageCreate() in gd.c.  

There are three different memory allocations within that function.  Line
numbers are from gd 2.0.35:


 83:  im = (gdImage *) gdMalloc (sizeof (gdImage));

 90:  im->pixels = (unsigned char **) gdMalloc (sizeof (unsigned char *) * sy);

104:     im->pixels[i] = (unsigned char *) gdCalloc (sx, sizeof (unsigned char));

I can easily see how if one of these is leaking than all could be leaking.
Valgrind is reporting two different stack traces for two different leaks,
those at lines 83 and lines 104.  But it's presumably the same single thread
of execution for both.

I don't know why valgrind is not reporting the case at line 90.  Perhaps it's
not really being leaked somehow, but I suspect that it's just a deficiency in
valgrind that it's not finding it.  And it's only a single byte, so it's not
likely to be noticeable in my approximate leak size comparisons anyway.

I'll reply again when I have a concise reproducible test case to file.

- Rich
Tamas Szekeres

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
2008/2/22, rich.fromm <nospam420@...>:

>
> The actual memory allocation happens in gdImageCreate() in gd.c, which
>  returns
>  gdImagePtr im.
>
>  This sets gdImagePtr img in msImageCreateWithPaletteGD() in mapgd.c, which
>  returns it.
>
>  This then sets gdImagePtr gdPImg in msSaveImageBufferGD() in mapgd.c.  This
>  value is NOT directly returned.  Instead, it is used to set unsigned char
>  *imgbytes, and that is returned.
>
>

I think this might be the case. Would you try adding something like:

Index: mapgd.c
===================================================================
--- mapgd.c (revision 7383)
+++ mapgd.c (working copy)
@@ -3686,6 +3686,8 @@
       gdPImg = msImageCreateWithPaletteGD(img, palette,
gdImageSX(img), gdImageSY(img));
       msImageCopyForcePaletteGD(img, gdPImg, method);
       imgbytes = gdImagePngPtr(gdPImg, size_ptr);
+      if (gdPImg)
+        gdImageDestroy(gdPImg);
     }
     else if ( force_pc256 ) {
       gdImagePtr gdPImg;
@@ -3704,6 +3706,8 @@
       for( i = 0; i < gdPImg->colorsTotal; i++ )
         gdPImg->open[i] = 0;
       imgbytes = gdImagePngPtr(gdPImg, size_ptr);
+      if (gdPImg)
+        gdImageDestroy(gdPImg);
     }
     else
       imgbytes = gdImagePngPtr(img, size_ptr);


If this fix is helpful then attach it to the ticket please.

Best regards,

Tamas
_______________________________________________
mapserver-users mailing list
mapserver-users@...
http://lists.osgeo.org/mailman/listinfo/mapserver-users
richf

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
Tamas Szekeres wrote:
I think this might be the case. Would you try adding something like:
I have the following six testcases:

1) Data from PostGIS, AGG renderer, FORMATOPTION "PALETTE_FORCE=TRUE"
2) Data from PostGIS, AGG renderer, no forcing
3) Data from PostGIS, GD renderer
4) Data from *.shp file, AGG renderer, FORMATOPTION "PALETTE_FORCE=TRUE"
5) Data from *.shp file, AGG renderer, no forcing
6) Data from *.shp file, GD renderer

1, 2, 4, and 5 all leaked.  3 and 6 did not.

In other words, PostGIS vs. *.shp did not matter, PALETTE_FORCE vs. not did
not matter, but AGG (leak) vs. GD (no leak) did matter.

I tried applying the following patch to mapserver 5.0.0:

--- begin ---
--- mapserver-5.0.0-etch/mapgd.c 2007-08-21 11:54:21.000000000 -0700
+++ mapserver-5.0.0-etch-bugfix/mapgd.c 2008-02-22 11:19:41.000000000 -0800
@@ -3696,6 +3696,8 @@
 
       msImageCopyForcePaletteGD(img, gdPImg);
       imgbytes = gdImagePngPtr(gdPImg, size_ptr);
+      if (gdPImg)
+        gdImageDestroy(gdPImg);
     }
     else if ( force_pc256 ) {
       gdImagePtr gdPImg;
@@ -3714,6 +3716,8 @@
       for( i = 0; i < gdPImg->colorsTotal; i++ )
         gdPImg->open[i] = 0;
       imgbytes = gdImagePngPtr(gdPImg, size_ptr);
+      if (gdPImg)
+        gdImageDestroy(gdPImg);
     }
     else
       imgbytes = gdImagePngPtr(img, size_ptr);
--- end ---

Now none of the cases leak.

These have been confirmed both with iterative runs (500 iterations) while
watching memory consumption in top, and with a single run using valgrind.

I will file a bug.

Let me know whether this information is sufficient, or if you want me to
package up a self contained testcase with the cases I used to show the
behavior both before and after the fix.  It will take me a bit of effort to
get everything into a form to package up and file, but it won't be all that
hard, and I'll gladly do it if you'd like.

Thanks very much for your help.

- Rich
richf

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
rich.fromm wrote:
I will file a bug.
It's bug 2525:
http://trac.osgeo.org/mapserver/ticket/2525

Let me know if you want me to attach a complete self contained testcase.

- Rich
Tamas Szekeres

Re: Antwort: Re: leaking memory in java mapscript (was Re: thread safety)

Reply Threaded MoreMore options
Print post
Permalink
Rich,

Thank you for your efforts, that was pretty sound :-)
I'll take over this bug and apply the fix this evening.

Best regards,

Tamas



2008/2/22, rich.fromm <nospam420@...>:

>
>
>  rich.fromm wrote:
>  >
>  > I will file a bug.
>  >
>
>
> It's bug 2525:
>  http://trac.osgeo.org/mapserver/ticket/2525
>
>  Let me know if you want me to attach a complete self contained testcase.
>
>  - Rich
>
>
>  --
>  View this message in context: http://www.nabble.com/thread-safety-tp15514019p15641420.html
>
> Sent from the Mapserver - User mailing list archive at Nabble.com.
>
>  _______________________________________________
>  mapserver-users mailing list
>  mapserver-users@...
>  http://lists.osgeo.org/mailman/listinfo/mapserver-users
>
_______________________________________________
mapserver-users mailing list
mapserver-users@...
http://lists.osgeo.org/mailman/listinfo/mapserver-users