• Post Reply Bookmark Topic Watch Topic
  • New Topic

Easy stringbuffer questions  RSS feed

 
Greenhorn
Posts: 25
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have legacy code where this is done often:

StringBuffer sqlStmt= new StringBuffer("");
sqlStmt.append("lots of stuff");
sqlStmt.append...again and again
sqlStmt = sqlStmt.delete(0, sqlStmt.length());
sqlStmt.append("lots of stuff");
sqlStmt.append...again and again
sqlStmt = sqlStmt.delete(0, sqlStmt.length());

Why use delete?
Why not simply:
StringBuffer sqlStmt= new StringBuffer("");
sqlStmt.append("lots of stuff");
sqlStmt.append...again and again
sqlStmt = new StringBuffer("");
sqlStmt.append("lots of stuff");
sqlStmt.append...again and again
sqlStmt = new StringBuffer("");
Let Java do own garbage collection.
 
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
(Has this got *anything* to do with servlets?)

I agree, create new StringBuffers. One problem with reusing StringBuffers
is that the char buffer may have grown to be huge and that capacity is no
longer needed. To that end, 1.5 added the trimToSize method. Be that as
it may, I still say create new StringBuffers and let the GC do its thing.
 
Ranch Hand
Posts: 308
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
When gc runs it stops almost all other processes unless you are using 1.5 and configured other cpu to do gc.

A good programming practice will be to reduce the number of objects created. I prefer setLength(0) or delete(firstIndex, lastIndex) over creating a new object.

Object oriented technology gives more emphasis to reuse. In that stand point also setLength or delete is a good choice.
 
author
Sheriff
Posts: 11962
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Moving to Java in General... Please continue the discussion over there.
 
author and iconoclast
Sheriff
Posts: 24217
38
Chrome Eclipse IDE Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by jiju ka:

Object oriented technology gives more emphasis to reuse.


Code reuse, not object reuse.
 
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In some JDK versions, StringBuffer has caused major memory leaks when people reused instances of it by means of setLength() or delete(). It's much safer just to throw it away and create a new one; they're not that expensive.
 
Java Cowboy
Sheriff
Posts: 16060
88
Android IntelliJ IDE Java Scala Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
As a side note: It is, in general, not a good idea to create SQL statements by concatenating SQL keywords and parameters together.

you should use PreparedStatement instead (look it up in the API documentation) - use question marks to indicate the parameter values in the SQL string and use PreparedStatement's set...() methods to set the parameter values.

PreparedStatement helps with performance (because it allows the database to cache SQL statements) and it makes your program less vulnerable to SQL injection (a security problem).
 
Jeff Albertson
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by jiju ka:Object oriented technology gives more emphasis to reuse.

Could this be an example of buzzword reuse?
 
jiju ka
Ranch Hand
Posts: 308
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Jeff Albrechtsen wrote:
I still say create new StringBuffers and let the GC do its thing


The "THING" gc does is release resources while 'new' allocate resources. In any programming language if you have a choice between 'reusing non-malign allocated resources' or
'releasing and re allocating same resources' which one will you use?

So the question here is really performance. If performance is a concern then I will certainly go with reusing the object.
[ November 16, 2005: Message edited by: jiju ka ]
 
Sheriff
Posts: 22846
43
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
On the other hand read what this article says about object pooling being unhelpful to garbage collection:

http://www-128.ibm.com/developerworks/java/library/j-jtp01274.html
 
Jeff Albertson
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by jiju ka:

So the question here is really performance. If performance is a concern then I will certainly go with reusing the object.


But what if performance leads you not to reuse objects? As mentioned,
before 1.5 StringBuffer didn't have a trimToSize method, so reusing a
StringBuffer could memory "leaks" ...

If it hasn't been mentioned in this thread already, Brian Goetz's article
Urban performance legends, revisited
should be required reading for anyone learning Java, to disabuse them of
what they might have picked up in the schoolyard.
 
jiju ka
Ranch Hand
Posts: 308
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for both the articles. It is informative.

Brian Goetz is saying allocation and garbage collection is getting better. I agree with it totally. In java it could be better than other programming languages.

If you run the following code you will get the following output


Below is the specifics of runtime

java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2)
Classic VM (build 1.4.2, J2RE 1.4.2 IBM Windows 32 build cn142-20040926 (JIT enabled: jitc))


Below is the output from the code above.

WorkFactor :500; Creation duration :16; Reuse duration :0
WorkFactor :5000; Creation duration :0; Reuse duration :16
WorkFactor :50000; Creation duration :15; Reuse duration :16
WorkFactor :500000; Creation duration :156; Reuse duration :47
WorkFactor :5000000; Creation duration :1578; Reuse duration :391
WorkFactor :50000000; Creation duration :15765; Reuse duration :3891
WorkFactor :500000000; Creation duration :173438; Reuse duration :58031
WorkFactor :705032704; Creation duration :226437; Reuse duration :56063
Done


You can see that the duration needed for allocating resource increases as number of objects increases.
[ November 17, 2005: Message edited by: jiju ka ]
 
Jeff Albertson
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The last post misses the point that pre-1.5, one couldn't release the memory
held in the StringBuffer's char buffer.
 
jiju ka
Ranch Hand
Posts: 308
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jack Adams question is not specific to 1.5

The answer above is specific to pre 1.5 If you run the above code in 1.5 you may get a different result.


Brian Goetz's article is not specific to 1.5


You can see that the duration needed for allocating resource increases as number of objects increases.


The last post just emphasis on the time needed for allocations.

Can you prove whether 1.5 can make the new StringBuffer loop of above program run faster?
 
Jeff Albertson
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Again, you're missing the point. Pre-1.5, if you used a StringBuffer, its
internal char buffer could expand, of course, but couldn't contract.
 
jiju ka
Ranch Hand
Posts: 308
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jeff, your points are not valid unless you prove it.
 
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This is something that's easier to just see for yourself by looking at the source. Here are relevant methods from StringBuffer in JDK 1.4:

It seems pretty clear here that the size of the char[] array "value" will not decrease, only increase. In contrast, JDK 5 has nearly identical code for these methods (except they've moved most of the methods to AbstractStringBuilder and have eliminated the sharing between String and StringBuffer instances). But JDK 5 also adds this important method:

Clearly this can reduce the the size of the backing array (in JDK 5+ only). However note that it's not called implicitly from any of the other methods. So setLength() and delete() still don't ever decrease the memory usage.

Now this isn't "proof" exactly. It's possible that somewhere you can find a JDK (from Sun or elsewhere) which allows StringBuffer to trim itself prior to JDK 5. If so, please let us know. Until then, though, I think it's a pretty good bet that in any given situation, a StringBuffer will not decrease in size prior to JDK 5.

[jiju ka]: I prefer setLength(0) or delete(firstIndex, lastIndex) over creating a new object.

Even under JDK 5, neither of these methods ends up calling trimToSize() (check the source!) so if at some point in the past you have created a Really Really Big String ™ with your StringBuffer, calling setLength() or delete() will not allow you to free up the memory in the value array. Ifyou're using JDK 5 you can solve this by calling setLegnth() or delete() and calling trimToSize(). Or you take the much simpler route of creating a new StringBuffer and not worrying about what version you're using, or whether or not you remembered to call trimToSize() after changing the length.
[ November 21, 2005: Message edited by: Jim Yingst ]
 
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator


It would seem setLength(0) would result in the buffer being brought down to a size of 16. That's not specified in the API though so it's not something you can rely upon, it's just an implementation detail that may or may not always hold true.

The proof is in the API. StringBuffer does not make any guarantees that it's backing will shrink, only that it will expand when necessary. There are also no methods that are specified to do so either prior to trimToSize() in 1.5. Consequently, the only way to be sure that a new StringBuffer doesn't have an excessive buffer allocated is to make a new one and let the old one be collected.
 
Jeff Albertson
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
And here I thought I was going to have some heavy lifting to prove it!
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
[Ken]: It would seem setLength(0) would result in the buffer being brought down to a size of 16.

Oops! Yes, that was careless of me. setLength(0) does have that undocumented effect, while setLength() for any other value does not.

Ken's point re: the API is good. Although it may be noted that trimToSize() does not guarantee the size will be trimmed. Then again, if we create a new object and discard the old one, we're relying on garbage collection, which also cannot guarantee that the memory will actually get freed up. In practice though GC usually works pretty well, and in practice trimToSize() generally does exactly what we'd expect it to. And in these cases the appropriate specifications at least give us reason to think that the memory may well be freed up. In contrast as Ken noted, for setLength() and delete(), the specs tell us nothing at all about memory usage.
[ November 21, 2005: Message edited by: Jim Yingst ]
 
jiju ka
Ranch Hand
Posts: 308
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Good points.

If the StringBuffer is created by new StringBuffer the boolean variable will never get a value of true. Because of this setLength(0) on a StringBuffer will never execute the code


On contrast "new String(buffer:StringBuffer)" will set shared variable of the StringBuffer to true. In the whole code this is the only place shared will be set a value of true.

This is really odd. This will make the resource allocation inconsistant.
For example setLength(0) in code 1 and code 2 behave differently.
Code1:


Code 2:


Referenced : Version 1.4 source code

Because setLength(0) is not guaranteeing resize and API documentation is not mentioning the 16 char resize for setLength(0), I will take Jeff's point "internal char buffer could expand, of course, but couldn't contract" as proven.

Thanks to Jeff, Kim and Ken. Coming back to performance, which among the following bottlenecks is favourable?
1. Time needed to Allocate and release memmory
2. Wastage of memmory by keeping memmory at the maximum used size.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!