WebObjects • 50:07
This session focuses on how to optimize and tune your WebObjects application. Learn about tools and techniques available for WebObjects 5 which collect and analyze application performance and identify areas of improvement. Practical tips for improving WebObjects, EOF, and Java performance are provided.
Speakers: Brian Fitzpatrick, Rich Flewelling
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
So if we could take our seats. We'd like to get ready for the next session. This is always a popular session with a very lively Q&A. And so I would like to introduce Brian Fitzpatrick and Rich Flewelling, optimizing WebObjects applications. Thanks Bob. It's always nice to present this sort of advanced session. We get some people who have been with WebObjects for quite a while.
And although some of the material can be a little bit dry, so Rich and I have got a stack of t-shirts we want to give away here for starters. And since some of you people have been with WebObjects for a really long time, we're going to start off with a little question and answer, multiple choice.
So when you see the answer that you think is the proper answer to this question, I want to hear you start clapping and Rich is going to determine who answered the question right. And then we're going to whip out a t-shirt. So first, what was the name of the event where WebObjects was introduced? Was it A, Web Hysteria? Web Mania? Webaholics Anonymous or D, WebWorld.
I think I only heard one person. I heard one. I think it might have been in this area. The answer is B, WebMania. Next question. Now this is actually more related to optimizing your app. How many classes load when you start your WebObjects application? 1 to 100, 101 to 1,000. 1001 to 2000. All of them. Again, the correct answer is C, 1,001 to 2,000 classes. Center? In the center over there.
Next question, what's the longest method name in WebObjects? Is it the ever popular add object to both sides of relationship with key? No? Is it the delegate method? Database context will run login panel to open database channel. is the other delegate method. Adapter channels should construct stored procedure return values. Or the often obscure, rarely called, object store coordinator should wait... I'll go ahead and let the translators catch up on that one. I'll go in this section. You guys haven't had one yet.
Oh, empty row. Whoever can get it first. What's that? Whoever can get it first. Whoever can get it first? Yep. Let them fight over it in the back. We have two more questions. What's in Bob Frazier's backpack? Is it A WebObjects marketing plan? Is it B, a ham sandwich? Is it C, Steve Heyman's shirt? Or is it D, another hat? What do you think? I don't know. What do you think? I wasn't... I'll go left side here.
A little bit over there maybe this time? Oh! Oh! And our last question before we get started. What is the most popular error that EO Modeler throws? Is it A, selector not found? B, NS internal inconsistency error? C, NSMutableArray, index out of bounds. Or D, NSBoy, I really hope you saved this a few minutes ago. You've got a pretty good arm there. Not bad, huh? Not bad at all. Now I'm going to turn it over to Rich Flewelling to talk. Let's get you started on the optimization.
Okay, so now to bring it down a little bit and a little bit more cerebral, I guess. What we're going to talk about today is to find and eliminate performance bottlenecks within your application. And the main areas that we're going to look at is what are the tools that WebObjects gives you out of the box to help you identify any trouble spots.
If you look at the architecture of a typical WebObjects application, where are some of the bottlenecks that typically pop up, and how can you go about eliminating those problems? So what we'll be doing today is looking at when is the right time within the application development lifecycle to go about investing your time in performance tuning.
Looking at WoE Event Center which comes with WebObjects to help you go about and tune your application. And a third-party tool from Borland called OptimizeIt that also can help you in this regard. And we'll also be looking at, specifically within the application server tier, at the WebObjects and EnterpriseObjects frameworks.
Techniques of using the software that we give you, techniques that help you improve the overall performance of the application server. And once you've done all that, there are times when you actually just have to upgrade your hardware and we'll show you some of the Unix utilities that can help you identify those problems as well.
So first of all, when should you start tuning when you're developing an application? You probably don't want to start tuning your application early on because you really have a bunch of functionality that the users are looking for to be implemented in the system and make sure that it's functionally stable.
So functional stability is the first thing to look for, but you shouldn't just go out and develop functionality without thinking about, well, what's a good design to go about implementing this so you're not just fetching things from a database constantly writing to various files on the system, but do things in a way that will give you 80% of that performance that you need and then go about tuning it once you get all the functionality that you want. So another key piece is to set your milestones for what you want your system to perform in terms of benchmarks. So that way you can go back and measure and do some optimization, measure again, and see the progress that you're actually making. actually making, if any.
So from the infrastructure side of the house, if you have the luxury of being able to get enough Hardware and software in place that can actually mirror what your deployment environment would be in a test environment, that's the ideal because that way any changes you make in your test environment, when you roll it to the deployment environment, you'll see the performance improvements just like you saw in the test environment. Likewise, having a test environment allows you to eliminate a lot of the extraneous variables of different users hitting your system while you're going through testing. This allows you to isolate and test for a specific thing.
On the other side of it, from the software side of it, we mentioned earlier that you should establish some acceptance criteria. And make these concrete. Go about and put down some numbers. For example, all pages should return in under half a second. And on this BigQuery search that we're going about and querying, the users will accept something in under 5 seconds or under 10 seconds, whatever that number is.
But put those in your requirements documents so that you can then test for the performance of the system and come back and verify that your system is performing like it should be. And then create some test cases in your system that will allow you to exercise these portions of your application and actually find out if you are meeting these criteria or not.
So we saw this slide earlier, and this is one of the most complex architectures that you could probably have with a Web application, but it really points out all the different places in this chain that can really become the weakest link for the performance of your system. From the time that the user hits a button on the Web browser and the request is sent through, look at all the different hops this request can go through. You can go through the Web server tier, firewalls, networks between the various tiers, through your application instances, perform some kind of functionality, searches out of the database, and then the round trip back.
So what we're going to do in the next few slides is look at each of these tiers and point out a few tips for things that would help you get the best performance out of each of those because ultimately, wherever your weakest link is in the system is going to be the weakest link for the performance of your system. So keeping all these things tuned as closely to optimal as possible will give you the best performance overall.
So let's look at the browser or the client desktop that your end users are using. One of the major factors is connectivity to the back-end system. So are they over a dial-up line or are they on an internal LAN? Are they on some ISP with a very fast connection? Those really have a lot of impact on just sending the requests and receiving the requests.
But there's also a big performance impact on what size of the pages are coming back to the desktop and what's in those pages. For example, are there many images that then, once the HTML is brought to the browser, the browser has to go out over the same connection and pull those things back? Is there a bunch of JavaScript that's embedded in your pages that needs to be parsed and processed on the desktop? One way to overcome that is to use cascading style sheets, which can really help you limit the amount of HTML.
But that also incurs an overhead with the browser needing to parse through those things. Likewise, the hardware on the client side can be affected based on what kind of browsers and plug-ins are being pulled in. You might have Flash movies that are playing. You might have QuickTime movies. So all these things are factors down on the client side.
Overall, we also need to look at the server sizing for each of the boxes. And as you know from computer science classes, you have resources of CPU memory, memory, and disk. And really the memory is virtual memory. And you really never want to have to use the virtual side of that memory. You want to be able to keep everything resident in physical memory.
And since we're in a networked environment, the network also plays a huge effect. So what we want to do is make sure that the processing that we have going on on each of these servers is using those resources in the most effective manner. So there's a couple of utilities that Unix gives you and also Mac OS X gives you. For example, Top, I think Avi mentioned that in the keynote, is something that he uses when he evaluates applications. And what we're going to do is go through. I'm going to do a demo.
We're going to go through the demo machine, which is demo 4, and show you we have an application here for this first demo that will... Show our need for speed against the movie schema, which I'm sure you're familiar with the example. In the movie schema, what we have is a bunch of movies and the roles of the actors that were played in that movie, and so there's an EO model behind all this.
For this first demo, though, we're not really using the EO model. What we're doing is we have something that will go off and do a ton of computations in the background, which we basically are saying is spamming the CPU. But first of all, there's a couple of utilities. CPU monitor, if you can see up here in the right-hand side of the screen, can show you the usage of the CPU.
And the blue line is user space, which is where we're running this application in the background, and the red is the operating system. And then I've started up top with a couple other parameters, dash U, which gives me a ranking by CPU, and it's refreshing every five seconds, and a dash D parameter on the top command, and you can look this up in the man pages, but that gives you the deltas between the last snapshot and the current snapshot. So what we're going to do is set up a... off this This is a request.
And you'll see immediately in the CPU monitor how we've pegged the CPU. And if you notice here, The Java application instance, which is the application server in this case, has risen to the top of the stack of the processes that are running on this machine. It's using 87% of the CPU. The load average on the machine at the top has really peaked, and now it's finished processing, and so the processing time has come back down. So this is one of the tools that you can use.
VMStat is another that allows you to look at virtual memory and applications. There's a variety of other tools as well out on the net that you can bring over to the platform. So that's the first demo. I'm going to turn it over to Fitz now and I'll be back up for the third demo after that. Thanks.
Thanks, Rich. Got to find my remote control. Now we're going to talk about a couple of issues related to your web server as well as your database server and how we can profile your app using some of the tools that come with WebObjects, namely WoeStats and WoeEventCenter. So first we have your web server.
Now, that's something that's often ignored because you're very focused on the code. There's a number of different things you can do with your web server to increase the performance of it. You can increase the RAM and file cache sizes. Again, as Rich said, you want to make sure you're never digging in a swap on a production server.
Because your performance goes down to zero pretty darn quickly at that point. Install the biggest, fastest drives that you can because if you're going to be reading data off the drives as well, serving your web server resources, static images and the like. Disable reverse DNS lookups. This is related to logging. It takes time for your web server to go back and look up the name corresponding to the number of a request that's coming in. And important not only for optimization but security, you want to make sure you're up to date on your operating system patches.
I'M GOING TO GO WITH THE We talked about disabling reverse lookups. If your web server, for example, Apache, is generating logs for every request and you're never going to use these, turn it off. That's the best thing you can do. You'll increase your performance and the ability to scale. If you are going to use it, see if you can't, log it to a separate disk or make sure you're on a fast disk subsystem.
Increase the TCP listen backlog. Now, basically, once you start getting many multiple requests per second, users are going to start backing up, not only queuing up in your app, but queuing up on the operating system level. And after a certain point, the operating system will start to refuse requests.
So by increasing this, you can make a longer queue. It's not going to make it any faster, but at least the users won't get the not available or machine not contacted message. Use the latest releases of HTTP server. I'm happy to say that Apache 2.0.36 was released on Tuesday of this week, and it does compile and run beautifully on Mac OS X.
And web objects, the CGI bin adapter does work with it. However, we're working on getting other on the, we call it mod web objects, working with that. For additional performance info, specifically related to Apache, you can take a look at the Apache website at the page right there.
Database server. On the back end, you've got your database server, and there's a lot of different things that you can do to tweak out your database server to get every little bit of performance out of it. A lot of different vendors recommend particular philosophies with how to set up your database server, where to put data files, what kind of hardware to use, how to tune memory allocations. In fact, Oracle recommends a whole number of kernel configurations for your machine to make sure that they can get every last bit of performance out of it.
Query tuning. If you turn your database on in monitoring mode so that you can keep an eye on what's going on with the database, you can see what queries are coming in. What can we do with the database to make each query go faster? Do we need another index somewhere? Your primary keys obviously should be indexed, but if you're querying on a particular attribute often, you may want to create an index on that. Cache data into memory. I've been to some places where they get this great big beefy database server with a map. They've got a massive amount of RAM in it, and they manage to squeeze their entire database into RAM. That's one way to speed things up for certain.
Now, application level tuning. When you're looking at your WebObjects components, you want to find out which one's slow. Obviously, clicking through the application, you can find out pretty easily what's taking the most time out of your app. However, Woe Stats is something that's going to be in your application and you can access this page to tell you how much time, down to hundreds of a second, each page is taking to render, as well as a minimum, a maximum, and an average request time. Woe Event Center can give you detailed statistics on your app on the component and the sub-component level and show you how many times each sub-component was referred to, how many times each component was hit.
And lastly, if database access might be the thing that's slowing you down, and Rich is going to talk more about this in the next demo when he talks about EOF, batching, and prefetching, and that sort of thing. But if you set EOAdapterDebugEnabled to true, you can see all the SQL that is being generated by your app and sent to the database. And in addition to that, WebObjects will have a little timestamp.
It'll say the minute and second that your requested database started and then the minute and second that it finished. So you can see, that'll show you A, perhaps your one query is taking a long time. Or B, perhaps by firing a lot of faults, you're doing a whole bunch of round trips to the database, which is not ever really a good thing.
So, as I said, we can view the transaction times via WoeStats, turn on event recording in Woe Event Setup. Now, Woe Event Setup is like an on-the-fly profiling tool. Anybody from here from the Ejective C days remembers GProf. Crank your, compile your application with profiling, crank it up, stop the application, examine the data.
Well, go to Woe Event Setup, set up the, tell the application to log all these events. And it, it, WebObjects can log just an absolutely ridiculous amount of events per second without a serious performance hit. And then it'll start monitoring all this. You can come back to the Woe Event Display page and see what it's got to offer.
So this will help you pretty easily detect which pages don't meet your acceptance criteria. You can find out exactly what the performance problem is. But remember to evaluate the times relative to the entire time. If you have a request that's taking 10 seconds, and 8 of those seconds are because of database fetch, and 2 of those seconds are because of a little calculation, a sort or something you're doing on the side, you'd probably do best to focus on the database fetch. Focus on the larger of the two problems in this case. Now, if I could have the demo machine back up here.
I'm going to go ahead and do a little demo here with using WolfStats. So let's close this up. We'll come into our application here and check out demo number two. And first thing I'm going to do is I'm going to go into WoEventSetup, which is password protected. So we'll log in here.
And you see there's several different kinds of events that you can monitor or you can pick up if you're looking for something in particular. What I'm going to do now is set them to all. We'll take a quick peek at the event log and you'll see that it's pretty much just logged the event of me coming to this very page. So that being done, we'll go back into the application.
and what we'll do here is we'll sort some stuff. Now, obviously this is a little contrived. We know that we're sort-- I'm hitting this one component. There's something that's taking a lot of time. You can see the CPU is cranking up again. But--so I have an idea of where to look, of what's going on.
But when I go look at Woe Stats or Woe Events Setup, it's going to tell me exactly which component, the name of the component that's taking so much time to complete and what's making the expensive calculation here. Okay, so we sorted 25,000 items and we took a good chunk of time. So let's go take a look at Woe Stats.
Log in again. Now, you'll see at the top, what stats tells you some nice information here. Overall transactions, component action, how long your app's been up. It tells you some great information down here about sessions. How long have your sessions been idle. So, and the session stats are something that can help you to tune your session timeout, which is something you should get as low as possible to avoid your sessions keeping all the memory around for that stuff. Total memory usage. And now we get down to what I'm looking for, the component action stats.
You'll see here, we have the name of the component, how many of them are served, the minimum, average, and maximum time. So, stats demo. Okay, there's nothing really big going on there. Utils demo that Rich did, we saw that that was pretty slow. Wow. And here, oh, bubble sort.
Okay, so we probably don't want to be doing a bubble sort in this application, so let's go ahead and change that over to a quick sort. And we'll save that. Again, rapid turnaround, not going to restart the application. We just go back in, and we'll try and sort our data again.
Okay, that was a little bit faster right there. That's quite an improvement actually. I think I should get a raise, Rich. Anyway, WoEventDisplay. Let's take a quick look at what WoEventDisplay has to tell us about all the stuff that's happened here. Okay, let's open this up. and we've got all these different information.
Here's the events it's logged and here are the comments, the classes that they come from. It keeps that information, how much time it takes and how many times it was called and relative to the whole. So if we go in our stats demo which we were just working on, you can see that our table element which contained our Sorry about that.
Which contained our Aqua button which invoked the quick sort bubble sort is what took the most amount of time. That's Woe Stats and that's Woe Event Center. Now what I'm going to do is turn it back over to Rich to show us a little bit more about EOF and what you can do to tune your database access. Don't forget to restart the app.
So what we're going to do now is we talked earlier about all the different application tiers within this system for a typical WebObjects app. And what we're going to do now is focus on the WebObjects app itself. And as you know, there's multiple layers that your software goes through to talk to any database. EOF makes it so easy to talk to databases that we want to leverage that software that's available, but we also want to use it in the most effective manner.
So what are a few things that we can do for Enterprise Objects Framework to improve performance? Well, first and foremost, make sure that you're not traversing from your application server to the database more times than you really need to to bring in the data or send the data to the system. And a couple ways of helping that is with batch vaulting and prefetching, and we'll talk about what each of those techniques bring to the table in the next few slides.
Another way to go about this is with the shared editing context. And how many people know what a shared editing context is? Okay, a pretty good number. And the biggest benefit for the rest of you is that it allows you to bring read-only memory into the application instance one time and use that across multiple sessions. So each session within your application doesn't need to bring in that same set of data and use it over and over, for example, in things that don't change a lot in your application.
Like the pop-up lists, different browsers, select items. You want to read that stuff in once when the application loads and leverage that throughout the app. If you have the luxury of making your application read-only, that would be a tremendously fast application because, again, you could bring most of the things into a shared editing context, operate on it there, for example, in a reporting application. If you could make everything read-only, that would be extremely fast. And ultimately, as I mentioned earlier, you just want to eliminate the constantly fetching the same data over and over.
Another way to go about doing this is using raw rows. What raw rows allow you to do is eliminate the overhead of taking the data from the database and then turning it into business objects. This is a great advantage in cases where you're not going to do a bunch of changes on that data and then persisting it down to the database or leveraging the business logic in the objects that are resident in WebObjects.
in your application, but you just want to bring in the data. For example, reporting, again, is a great example. Bring the data in from raw rows, use it as is, and go on your merry way. Another thing that you should use for your application to protect your application from users doing huge searches, searching and trying to bring in all the data from the database into your application instance and bloating memory is to set fetch limits.
For example, you might determine that for your application a user really can't operate on datasets more than 250 records. It just is unwieldy, and you want to give them some feedback on how to restrict that search down. So fetch limits within EOF really can help you do that, and it helps you eliminate unrestricted searches within your application.
If you have very large object graphs, making changes to these object graphs becomes a problem. What you really want to do is have small sets of data that are being operated on. And one way to do that is with nested editing contexts. And another is with peer-to-peer editing contexts. This allows you to encapsulate the data that's going to change and operate on it in a very small, restricted fashion, make the changes there, and then send them to the database instead of having this very large set of data that the editing context is trying to manage.
A couple ways to keep data within your application from going stale. And this can happen in a case where, for example, you have an admin application that makes changes to the database and then you also have an Internet application that is leveraging that data. In many cases, you make changes from the admin application in the database and you want that data to be replicated out to the Internet as quickly as possible.
One way to do that in a brute force method would be to use invalidate all objects on the Internet instances. And what would happen in that case is it would tell EOF that all these snapshots within my system are invalid and so next time somebody comes to access this data, you need to refetch that data and bring the snapshots up to speed. And so that's one way to go about doing that.
Another way, though, is to, instead of doing a brute force method on the entire object graph, is to bring that into a more restricted set of EOs, if possible. And there's a couple methods that you can set on a fetch specification. Set refreshes, refetched objects to true. And so you create a search, you bring those data objects in, and you set that to true, and that will then refetch that subset. So instead of working on the entire data set that you had in your application instance, you can restrict that down to, ultimately, to things that have actually changed.
Another way to do this is to set a time lag on the snapshots themselves. The default value out of the box is one hour. That might meet your needs fine, but in cases where you're making changes rapidly to the admin side and you want to see this reflected, you probably want to bring that time stamp down. But understand that the smaller you bring that time stamp down, that will cause fetches to the database to happen more often.
So it's really up to you guys to determine what's the most effective for the functionality you're trying to perform. So let's go through a demonstration of batch faulting and prefetching. Great, we have the demo application up. First thing I need to do is restart the application to clear out any of the changes that were made earlier. Um, and so that'll just take a second.
And you'll notice that I'm running everything on this laptop. So some of the times that you see in the searches will be pretty large in terms of seconds because I have the browser, project builder, the application, the database, everything running on this one machine. So let's go into the application again. And we're on demo 3.
The first thing we want to do is go through a couple different fetch mechanisms against the database. If you remember earlier, I mentioned that what we're using is a movie object and it has a one-to-many relationship to the roles in that movie. The first way to go about doing a fetch is a regular fetch.
Go out, search for a bunch of movies, and then pull in the movie roles for that. If you look at, you can see how long it took to refetch here, and if I go down here to update, You can see that this took 5.65 seconds to return that data set.
And the data set that's returning, there's about 100 movies and about 450 rolls for those movies. And if we look, I've also turned on SQL debugging. So what you can see here is how many times It has had to go to the database to do selects, so let me highlight one here.
What you can see is it's selecting a movie roll, and again it's selecting a movie roll, again it's selecting a movie roll. So it's going across this bridge from the database, or across the network from the database to the application server many, many times. So let's look at a better way to do this, or one better way to do this.
Batch Vaulting. Here again you can see in the background the number of round trips to the database, but the performance was much better in this case. And we're down to 1.73 seconds. And so what's happening in this case is that movies are being selected and then when I go to fault in the movie roles for a particular movie, I also tell the system within EO Modeler to go off and bring in a batch of movie roles next time you go across that when you go to the database. So I'm telling it to bring in a set of 20. And this really cuts down the round trips from 100... 100 and 1 for the regular fetch down to about 20 for the batch faulting case.
In the third case, we can use prefetching. In this case, we have the fastest performance because it's only having to go to the database two times. One to get the movies because we're doing a qualified search, and then another time to get the movie rolls for those movies. We only go to the database twice, and you can see what the time is in this case is 0.74 seconds. To help you compare and contrast each of these, we can go through a little bit more detail about each of the different fetches.
As you see at the top one, we do one round trip for the movies and then one round trip for each fault fired. That's the slowest performance. Batch faulting helps you cut that down by when you go to the database for the faults, you say, hey, give me a collection of those every time instead of taking them one at a time. That really helps you bring down the time. The last is prefetching. In this case, you create a fetch specification. You create the movies, and you tell it that I want to follow a key path.
You then send the movie to the movie rolls. In this case, it brings in the movies and then sends off one more trip to the database to pull in all the movie rolls associated with those in that first collection. That's why we get the best time for that case.
There's one other part to this demo, raw rows. And we mentioned that earlier that it's a great use for reporting. And so if we go off and search, you can see up in the right-hand corner here, we have, pardon? Thank you. I need to restart because I already have cached a bunch of data here.
This will just tell me to start my session again, so we'll do that. Okay, so we're back to the raw rows part of the demonstration. And we'll click on the rows. We bring in 453 movie rolls, and you can see the time in that case is 0.44 seconds. And if we turn that data that we pull back into objects, there should be some additional overhead associated with that, and that comes back in 0.96 seconds.
When you want to use these, there's two different scenarios. One is you just want to interact with the data itself. You don't really need the business logic that the EOs give you. And the second case is you want to make a bunch of changes to things and leverage the business logic. And so that's when you should incur the overhead of the 0.96 seconds.
Now remember, these times are way out of whack because this is all running on this one laptop in a real environment. These would be probably orders of magnitude smaller. But it gives you the effect for what the changes are. So with that, I'll turn it back over to Fitz. Thank you. Thank you.
Thanks again, Rich. Now, I'm going to focus now on a little bit, a little bit on things a little bit lower. Anyone here go to the Java performance tuning sessions? Okay, that was a big, real big hit, I can see. We're going to talk a little bit about what you can do down in the language level to improve your performance of your application. And finally, we're going to talk about bad algorithms. For example, you saw, you know, I was sorting 25,000 objects using bubble sort, which takes a little bit of time.
Memory demands. Are you pulling tons of objects into your application that are unnecessary? Memory leaks. Do you have circular references? What can you do to help the garbage collector out and make it easier for it to clean up as you run out of memory? And finally, which I'm really excited about showing, is using OptimizeIt to look inside the Java virtual machine at your app.
Is there anyone here who's used OptimizeIt? Can I see a show of hands on that? Okay, that's great. Having used OptimizeIt, it's like you realize that before that I was just looking at this blind ball of mud, which was the JVM, and I was kind of guessing at what was happening based on my code. But now I'm going to show you how to use OptimizeIt to look inside the Java virtual machine and see what's going on.
So improving the performance of your app. First of all, tune the JVM heap size. And then test it with the different deployment environments that you're going to use. Remember your development environment, your laptop or your workstation, is going to be different than the machine that you deploy the application on.
The heap size does play an important role because when every time that your application has to increase the heap size, if it's not at maximum already, it incurs a small performance penalty because the garbage collector is going to run either a little bit or a lot. One of the things that I recommend and that a lot of actual vendors do recommend with Java application servers is to set the minimum heap size to the maximum heap size. The minimum heap size tells your app what to malloc when it first starts up.
It says grab this big hunk of memory. The maximum says keep grabbing until you hit this number and grab no further. So if that memory is there, if it's available for your Java app, it's not going to be used by anything else. And it shouldn't because if it's used by something else, you may max out and go into swap, which as we know is death. So minimum heap size, maximum heap size.
Now in WebObjects 4.5 we had the nice verbose NSJavaMinHeapSize and NSJavaMaxHeapSize settings, which have been deprecated because now there's no longer an Objective-C wrapper around your JVM. We're invoking Java directly. We're using the -XMS for minimum size or -XMX for maximum heap size. They default to 32 and 64, however you can change that if you'd like to.
Deployment. As Rich said earlier, examine the machine using TOP, VMStat, or other performance tools. See how much memory your operating system is using or any other tasks running on that machine. Configure your application to use whatever's left. So if you have a machine with a gigabyte of RAM and your operating system is using under 100 meg of overhead, it gives you about 900 meg left, so you can either give one instance 900 megabytes or nine instances 100 megabytes. Or in the instance of the Deutsche Bank guys, your E10K with four terabytes of memory, you can give 480 instances, whatever they need.
Setting references to null. When you're done with an object, not necessarily in the local scope, a local variable in a method is the garbage collector knows it's out of scope and it will deal with that appropriately. But fields in your classes, when you're done with this field, you know you're done with it, set it to null. Make one less reference for the garbage collector to have to navigate.
Be aware of finalize. Finalize is something that's going to happen during or after garbage collection and there's a penalty for calling finalize. It costs more time. You shouldn't be invoking it explicitly. Finally, if you must implement finalize, remember to call super. Otherwise, you're going to have some weird problems.
Now, I want to touch a little bit on garbage collection. And this is always an interesting and strange voodoo behind the garbage collector. And so I tracked down some of the JBM guys at Apple this week and I cornered them. I said, so what happens when you call system.gc? We've been told it's a hint.
And the specification for the Java virtual machine says that it doesn't have to run the garbage collector when you say system.gc. I've seen bizarre little bits of code. In fact, I've written some myself that call the garbage collector. And then check the free memory and call it again and check the free memory and keep doing that.
When you call systems.gc with Apple's Hotspot VM we ship with Mac OS X, it does a full garbage collection sweep. Now that was actually news to me. I thought it was just a hint. So when I say full sweep, that means we're suspending all threads and then doing a full sweep through the VM for anything we can get rid of.
That's expensive. It's time consuming. There really is no good time to do a full sweep of your application. I met a person who said, why are you so worried about this? In session terminate? I call system.gc and the session's done with. What's to worry about? Well, what about the other people? The sessions are currently running. We get to sit there while your garbage collector does its thing.
Now, we have a little demo here with OptimizeIt. And just to point out that there is... There is another application out there called JProbe, but Optimizeit runs on Mac OS X quite well, I might add, so we're going to demonstrate that. Now the first thing I'm going to point out with this is-- These guys are so good, I don't even notice them. I just assume things have changed, and it sure did.
When your application starts up, We've got, you see this massive, massive line here. Java, and here you'll see the XMX64, 64 meg Mac, 32 min, and then some different defaults, and a whole load of directories for your class path. All the frameworks it's going to pull in, the Java VM framework, the jar for your application itself. So what you do with that on, oh, I'm doing fine. What you want to do with that is copy that and create a shell script and modify that line a little bit. So we've got right here a little shell script that I've cobbled together.
[Transcript missing]
It says port is 1470. Here's a little bit of information that optimize is telling us. So what it's saying is we've opened port 1470 for you to attach with your optimize it suite to get information about the running application, about the JVM. Now it takes a good bit longer now because every action is being audited and all this information is being generated. So now I've got optimize it started over here and I'm going to attach to my running instance.
And in pretty short order, we're going to see the answer to one of our questions before. How many classes does your application load when it starts up? Well, first of all, we've got 21,000 instances of char array. We have 16,000 strings. We have enough string buffers to fill a truck. And if I scroll down here, these are all the classes that we've got loaded in our application. Notice I'm still scrolling. Still scrolling.
We've got WebObjects Foundation, Print Streamlogger, Apache Xerces stuff, Text Decimal Formats. And this is one way you can look at all the instances of your objects. You can get an idea of how much memory they're taking, how many instances you have of each object. We can come over here and this is actually my very favorite part here and we can find out how much heap size are we using? How much is available to us? How much is used? What's the garbage collector doing? How many active threads? For those of you who say, oh, my application isn't multi-threaded. It's impossible to write a WebObjects application that's not multi-threaded. You have 24 just from the start.
Didn't do anything special here, 24 threads. Loaded classes, 1,036. Did anyone else get surprised by this number? I was like, wow, you know, I knew there was a lot of stuff going on, but that seems like an awful lot of information. So what I'll do now is I'll kick back to this. We'll sort by instance count and we'll go into our running application, which is going to take a little bit longer to go through the first time because it's got a whole lot more information to be generated.
So, there we are. This is going to return now. And we're going to go to demo 4. Now, I've got a couple of different things here. The first thing I'm going to do is go ahead and leak some memory. Now, I've set my session timeout to a really short session timeout. And so at the end of your session, obviously, I've created a whole bunch of objects and put them in a cache in my session. And when the session times out, what this should do is release those objects.
So, we're going to give this a second here and the session will time out. Now, go back to optimize it and take a look. Now, if you peek and optimize it real quick and sort by instance counts here, we'll see that on the fly it's picked up some big object. And we have 15,000 of these that are taking up quite a bit of RAM. So, let's check here if this thing is ever going to terminate.
Did you turn session termination off on me, Rich? You can just cut this out, right? Should I do the Steve Hayman and say, bite me, bite me, bite me, or something like that? So, there we go. Our session finally terminated. Now we'll come back to optimize it. And notice everything's still here. Nothing's really changed. Nothing's been garbage collected. Why? The garbage collector doesn't need to run. There's a lot of objects that we can dispose of that we don't need.
[Transcript missing]
We can see where it's allocated. It's going to pull a backtrace and walk all the way down the call stack so you can see where this is referred to. Way down here, all the way in the bottom right-hand corner. Called from key value coding.
So there you are. Allocation, location, JVM demo leak. So I'm going to pop into Project Builder real quick and take a look at the JVM demo component. So, specifically, the leak method. So, we'll take a quick look. Here's our leak method. Okay, so, got a cache session and I'm stuffing all these objects into this cache object, cacheMutableArrayInSession. So, we'll come up here, we'll take a look at session. Here's our cache method.
Okay, underscore cache. Now I can't figure out why that's leaking yet. Let's take a look at the instance variable. Ah, so there's our answer. We've declared it statically. How foolish of us. So we can go ahead and fix that, save it, recompile the app, and it'll go ahead and allocate properly. So we'll go back here, and now for the really fun part that I like, is run out of memory.
I'm sure no one here has ever had that problem with the Java virtual machine, right? Ever run out of memory? Okay, what I'm going to do here is start allocating objects by the thousand. And they're basically just going to start consuming the virtual machine until it's forced to increase the heap.
Remember, our maximum heap is set to 64 meg. It's currently using the minimum heap. In fact, it's not even near that. In fact, I'll run the garbage collector and make sure everything's cleaned up. Garbage collector did a full sweep, 100% activity. Over here, about 8 meg used. So we'll come back to our app, and I'll say, run out of memory. And I'll rush back over here.
[Transcript missing]
Okay, full sweep, all the way to the top, right there. And we're getting pretty close, and now we're at the top of our heap. Slam into it, the garbage collector runs as hard as it can. Now this is all happening inside a WOL component, so our request handler is going to handle this, grab the exception, and then the garbage collector successfully deallocates all of those objects and dumps this back down. Now look how hard that's running. That is from time start 5 minutes and 30 seconds to 5 minutes and 50 seconds. And now we're back down to 8 meg. So that's my Optimizer demo for you.
I believe that's about it. Again, to be considered for the WebObjects beta, even if you've already been in a beta before, you should sign up at applec.apple.com slash webobjects. The lab is downstairs. If you haven't been to the lab, I recommend going by, especially if you have one of those impossible problems that's bugging you. Bring your laptop down there. We've got engineers from WebObjects, engineers from Java who will be glad to take a look at it for you.
Roadmap. We have a feedback session this afternoon, which I heard is going to be extremely entertaining, a feedback session. Much better than any of those other feedback sessions that we'll be having this afternoon. Who to contact? You can contact Tony Trujillo-Vian, Bob Frazier, Apple Professional Services, which is where Rich and I work. We give her training, support, and consulting. And more information, we have a whole bunch of URLs.