Last modified by Pascal Robert on 2010/07/28 14:21

Hide last authors
Denis Frolov 15.1 1 |=(((
2 Contents
3 )))
4 |(((
Denis Frolov 16.1 5 {{toc minLevel="2" style="disc"/}}
Denis Frolov 15.1 6 )))
Quinton Dolan 6.1 7
Pascal Robert 12.1 8 == Overview ==
9
Denis Frolov 15.1 10 Memory Management in Java is a bit of a black art. When combined with EOF, there are certain issues that you need to be aware of. In WebObjects 5.2, there was a major change to the memory management system in EOF that provided support for WeakReferences to EO snapshots rather than only relying on the previous system of reference counting. This provides for vastly superior memory performance under the same conditions with the same app in 5.2 and above, because there are many cases where references will be released by Java's memory system rather than be held onto by your EOEditingContext.
Pascal Robert 4.1 11
Ray Kiddy 17.1 12 There is a large overlap between the content about [[Caching and Freshness>>doc:documentation.Home.Development Architecture.EOF Architecture.EOF.EOF-Using EOF-Caching and Freshness.WebHome]] and that of Memory Management. Both should be read to fully understand how caching and memory management should play a part in your application design.
Pascal Robert 4.1 13
Pascal Robert 12.1 14 == General Issues ==
Pascal Robert 4.1 15
Denis Frolov 15.1 16 One important issue to consider prior to taking any drastic measures is whether or not your application really just requires more memory than you are giving it. By default, a Java Virtual Machine gives a Java application 64M of memory. This is not that much memory for an enterprise web application. You can adjust this by setting the "mx" flag on your VM at launch time (-Xmx256M would give you VM 256M of heap to work with instead of the default 64M). Other than just hunting aimlessly in your code, profiling your application is the only way to truly determine your application's memory situation.
Pascal Robert 4.1 17
18 The following profiling tools have been verified to work with WebObjects:
19
Denis Frolov 15.1 20 * JProfiler ( [[http:~~/~~/www.ej-technologies.com/products/jprofiler/overview.html>>url:http://www.ej-technologies.com/products/jprofiler/overview.html||shape="rect"]] ) has explicit support for WebObjects. It can automatically update your WebObjects Application launch script to insert itself at runtime to allow you to remotely attach to the application and profile it.
21 * [[VisualVM>>url:https://visualvm.dev.java.net/features.html||shape="rect"]] is a tool to monitor and troubleshoot Java applications. It runs on Oracle/Sun JDK 6, but is able to monitor applications running on JDK 1.4 and higher. It utilizes various available technologies like jvmstat, JMX, the Serviceability Agent (SA), and the Attach API to get the data and automatically uses the fastest and most lightweight technology to impose minimal overhead on monitored applications.
Pascal Robert 4.1 22
Pascal Robert 12.1 23 === Raw Rows ===
Pascal Robert 4.1 24
Pascal Robert 18.1 25 The use of [[Raw Rows>>doc:documentation.Home.Development Architecture.EOF Architecture.EOF.EOF-Using EOF-Raw Rows.WebHome]] can dramatically decrease your memory usage (and increase your application's performance) at the cost of convenience. This uses less memory than normal EO's, allows better support for fetch limits, and you can still promote them to full-blown EO's when need be. Check out the [[Raw Rows>>doc:documentation.Home.Development Architecture.EOF Architecture.EOF.EOF-Using EOF-Raw Rows.WebHome]] topic for more information.
Pascal Robert 4.1 26
Pascal Robert 12.1 27 === Qualify your Fetches ===
Pascal Robert 4.1 28
Denis Frolov 15.1 29 If you are not careful when you fetch, you may bring in huge datasets. When you use an EOFetchSpecification, be mindful to specify the most restrictive EOQualifier that still returns the data you need. Otherwise, you will be potentially faulting in much more data than your application will use, and you decrease your overall application performance because of the data that must be transferred from your database.
Pascal Robert 4.1 30
Pascal Robert 12.1 31 === Relationships from Enumerated Types to other EO's ===
Pascal Robert 4.1 32
Denis Frolov 15.1 33 It is very common to have EO's that represent "Enumerated Types". For instance, you might have a bug tracking system that has a Severity entity (with individual instances of "Crasher", "Minor", etc) that are referenced by all bugs in the system. It is tempting to always create relationship back-references (i.e. Bug=>Severity, Severity=>>Bug). However, be aware that if you create the reference from Severity back to Bug, any time a Bug is added and you properly use the addObjectToBothSidesOfRelationshipWithKey method, EOF will fault in the ENTIRE "bugs" relationship on Severity prior to adding your new bug to the end of the list. The consequences of this can be crippling to your application. As the number of bugs increases, it will take longer and longer to add a new bug. The recommended best practice for this is to only create Bug=>Severity and NOT create the reverse relationship. In the event that you need to perform that fetch, you can manually construct the EOFetchSpecification rather than use the automatic approach.
Pascal Robert 4.1 34
Denis Frolov 15.1 35 Note that this is a filed bug against EOF. One approach that EOF could use is to only add the object to Severity's bugs relationship if the bugs relationship has already been faulted into memory. If the array has not yet been faulted, then causing the fault on insert is potentially very expensive. The reasoning for the current behavior is to maintain the ability to access the newly committed Bug via the Severity bugs relationship prior to committing your transaction (i.e. faulting the relationship from the database wouldn't yet return the newly inserted Bug). This is very important to maintaining object graph consistency pre-saveChanges, but does come at a high performance cost.
Pascal Robert 4.1 36
Pascal Robert 12.1 37 === Dispose()ing your EOEditingContext ===
Pascal Robert 4.1 38
Denis Frolov 15.1 39 Calling dispose() on your EOEditingContext is not strictly required. When the EOEditingContext is no longer referenced, it will be automatically disposed. However, the question is how long it takes the GC to get to your object. If you call dispose() manually, you won't hurt anything, and you immediately decrement the snapshot reference counts. If your EC is falling out of scope or you set it to null, then it's probably a tossup as to who gets to it first - GC finalizing your EC or you manually calling dispose(). It is recommended that you dispose() your EOEditingContext when you are in a long running process in a single method where the EOEditingContext will not go out-of-scope until the end of the method.
Pascal Robert 4.1 40
Pascal Robert 12.1 41 === Calling System.gc() ===
Pascal Robert 4.1 42
Denis Frolov 15.1 43 Calling System.gc() is usually a bad idea and can cause performance problems in your application. Additionally, System.gc() is not an order, it is a request, so if your application is relying on this command to execute, you should re-evaluate your application's design, because you may run into problems with this.
Pascal Robert 4.1 44
Pascal Robert 12.1 45 === Anecdotes on editingContext.invalidateAllObjects() ===
Pascal Robert 4.1 46
Denis Frolov 15.1 47 It can be tempting to resolve memory problems by calling editingContext.invalidateObject(..) or editingContext.invalidateAllObjects(). Avoid this. You will only hurt yourself in the long run. Invalidating is very aggressive and WILL cause exceptions to be thrown if someone in another EOEditingContext is modifying an EO that you invalidate in your EOEditingContext (the shared snapshost will be destroyed, resulting in an exception on .saveChanges in the other context). It should only be used under very controlled situations where you understand the implications of the method.
Pascal Robert 4.1 48
Pascal Robert 12.1 49 ==== Chuck Hill ====
Pascal Robert 4.1 50
Denis Frolov 15.1 51 As for invalidating objects, this is something to be avoided. I admit that I have been driven to desperation a couple of times and used this, but only with regrets and reservations.
Pascal Robert 4.1 52
Pascal Robert 12.1 53 ==== Art Isbell ====
Pascal Robert 4.1 54
55 I just have a bias against using invalidateAllObjects() because, for me, it's caused more problems than it's solved.
56
Pascal Robert 12.1 57 ==== Mike Schrag ====
Pascal Robert 4.1 58
Denis Frolov 15.1 59 The couple of times I resorted to this, I regretted it later, because it ended up screwing me. The biggest thing is you toss snapshots that people might be in the middle of editing, and that's going to cause really funky problems. It's a really heavy-handed way to go.
Pascal Robert 4.1 60
Pascal Robert 12.1 61 === NSUndoManager ===
Pascal Robert 4.1 62
Denis Frolov 15.1 63 One of the most common memory problems people run into with WebObjects is the NSUndoManager. WebObjects has a particularly cool feature in that it supports an undo stack for EO transactions. Each time you saveChanges, you push an undoable set of changes onto the stack. The downside of this behavior is that you end up with strong references to old EO states, which can be deadly in a large EOF operation.
Pascal Robert 4.1 64
Denis Frolov 15.1 65 There are several fixes for this. One is to simply disable the NSUndoManager on your editing context by calling:
Pascal Robert 4.1 66
Pascal Robert 12.1 67 {{code}}
Pascal Robert 4.1 68
Pascal Robert 12.1 69 editingContext.setUndoManager(null);
Pascal Robert 4.1 70
Pascal Robert 12.1 71 {{/code}}
Pascal Robert 4.1 72
Denis Frolov 15.1 73 It has been referenced that setting the undo manager to null may cause issues with deletes under certain circumstances. In testing this with WebObjects 5.3, the situation could not be reproduced, but if you use WebObjects 5.2 or lower, you should be aware that you might run into problems.
Pascal Robert 4.1 74
75 The recommended alternative in this case is to disable the registration of undo events by calling:
76
Pascal Robert 12.1 77 {{code}}
Pascal Robert 4.1 78
Pascal Robert 12.1 79 editingContext.undoManager().disableUndoRegistration();
Pascal Robert 4.1 80
Pascal Robert 12.1 81 {{/code}}
Pascal Robert 4.1 82
Denis Frolov 15.1 83 Note that setting the undo levels to 0 using setLevelsOfUndo() does not work, as 0 means no limit!
84 Prior to 5.2, the NSUndoManager had effectively infinite undo levels by default. The javadoc of WO **5.3** still mentions the following:
Pascal Robert 4.1 85
Pascal Robert 12.1 86 //EOEditingContext's undo support is arbitrarily deep; you can undo an object repeatedly until you restore it to the state it was in when it was first created or fetched into its editing context. Even after saving, you can undo a change. To support this feature, the NSUndoManager can keep a lot of data in memory.//
Pascal Robert 4.1 87
Pascal Robert 12.1 88 However, as of 5.2, if you are using the session defaultEditingContext, WOSession sets the default undo stack size to 10 if there is no default size specified with the WODEFAULTUNDOSTACKLIMIT parameter.
Denis Frolov 15.1 89 Lastly you can manually clear your NSUndoManager when you are done with an operation by calling:
Pascal Robert 4.1 90
Pascal Robert 12.1 91 {{code}}
Pascal Robert 4.1 92
Pascal Robert 12.1 93 editingContext.undoManager().removeAllActions();
Pascal Robert 4.1 94
Pascal Robert 12.1 95 {{/code}}
Pascal Robert 4.1 96
Pascal Robert 12.1 97 If you use an NSUndoManager, it is recommended that you call this after performing very large saveChanges that involved large amounts of data.
Pascal Robert 4.1 98
aaronr 13.1 99 ==== Aaron Rosenzweig ====
100
Denis Frolov 15.1 101 In our experience, the NSUndoManager should be considered an integral part of an EOEditingContext and neither be crippled nor removed. Calling
aaronr 13.1 102
Denis Frolov 15.1 103 {{code}}
104 removeAllActions()
105 {{/code}}
106
107 is safe and can be done in your project's descendent of EOEditingContext after a successful call to
108
109 {{code}}
110 super.saveChanges()
111 {{/code}}
112
aaronr 13.1 113 In WO 5.3 and above, setting the NSUndoManager to null will create the following error if you try to delete and save an EO that has a delete rule of "deny" on an existing relationship:
114
115 {{code}}
116
Denis Frolov 14.1 117 java.lang.RuntimeException: java.lang.IllegalStateException:
118 Editing context needs an undo manager to recover from delete propagation problems.
aaronr 13.1 119 Do not set the undo manager of this editing context to null.
120
121 {{/code}}
122
123 Additionally, if you simply remove undo registration, you'll find that little undo / redo pieces are still allocated in memory... which defeats the purpose of removing registration. This can be verified with a tool such as Jprofiler, etc. Any tool which allows you to view the java heap and memory allocations.
124
Denis Frolov 15.1 125 If what you are really after is processing many EOs in a long running process you should consider making an array of primary keys and materializing them onesy-twosy in a temporary EOEditingContext. Before exiting the loop call
aaronr 13.1 126
Denis Frolov 15.1 127 {{code}}
128 ec.dispose()
129 {{/code}}
Pascal Robert 4.1 130
Denis Frolov 15.1 131 then
132
133 {{code}}
134 ec = null
135 {{/code}}
136
137 Another way to say it, make an EOEditingContext for each pass through the loop then clear it out before the next pass.
138
139 == WebObjects 5.2+ ==
140
Pascal Robert 12.1 141 This is a description of the current behavior in 5.3 that one might be able to gather if one were to, say, decompile and review the entire process - not that i would ever do this or condone it, of course - but if you DID, you would probably gather exactly this info, which is pretty well documented (and seems to behave to-spec) in the 5.2 release notes:
Pascal Robert 4.1 142
Pascal Robert 12.1 143 As of 5.2, the way it works is: The snapshots in EODatabase have a reference count. Each editing context that fetches an EO increments the reference count. The EC holds onto that EO via a WeakReference. When the WeakReference is reclaimed, the snapshot reference count can decrease (note CAN, not IMMEDIATELY WILL - the editing context keeps reference queue which is only processed periodically). When the count gets to zero, the database forgets the snapshot. If you have entity caching enabled, then EODatabase ignore reference count (or keeps it at "1" as a minimum) and it will not go away in a read-only scenario. If you modify any entity of that type and saveChanges in your EditingContext, a cached entity's cache will be entirely flushed. (NB: Keep this in mind, because if you are caching a large amount of data that is writable, it will NOT be very smart about updating that cache - It's blown away with every update and then it immediately reloads the entire set of objects for that entity at the next access)
Pascal Robert 4.1 144
Pascal Robert 12.1 145 If you have retainsAllRegisteredObjects enabled on your editing context, it will NOT use WeakReferences. Under this circumstance, the EO reference count is only decreased when 1) you dispose the editingcontext or 2) you forget or invalidate the object.
Pascal Robert 4.1 146
Pascal Robert 12.1 147 When you modify an object in an editing context, the editingcontext keeps a strong reference to the objects until you saveChanges (or revert, reset, etc), at which point the strong references are cleared and the only remaining reference is the weakreference like before.
Denis Frolov 15.1 148 If you have an undo manager enabled, it will keep a strong reference to the affected EO's as long as the undo is around.
Pascal Robert 4.1 149
Pascal Robert 12.1 150 I do wonder if EC's should be using SoftReferences instead of WeakReferences ... Would seem to be more friendly to the users of those EO's.
Pascal Robert 4.1 151
Pascal Robert 12.1 152 If you are using WO pre 5.2, then none of the WeakReference stuff applies, and everything is purely done with snapshot reference counting - it should behave like retainsAllRegisteredObjects = true in 5.2.
Pascal Robert 4.1 153
Pascal Robert 12.1 154 == WebObjects Pre 5.2 ==
Pascal Robert 4.1 155
Pascal Robert 12.1 156 Prior to 5.2, all snapshots were simply reference counted by EOEditingContext. If an EO was faulted into your EOEditingContext, it would not go away until the EOEditingContext itself was disposed. As a result, if you have an application deployed on a pre-5.2 WebObjects, you will need to adopt a strategy of periodically disposing your EOEditingContext and creating a new one.
Pascal Robert 4.1 157
Pascal Robert 12.1 158 === Chuck Hill ===
Pascal Robert 4.1 159
Pascal Robert 12.1 160 In our experience, EOF is not too good at bulk operations like this. If we have a lot to do, and don't need logic from the EO, we wander down to the EOAccess level, and whip up some batch insert statements. You might even want to consider straight JDBC.
Pascal Robert 4.1 161
Pascal Robert 12.1 162 OK that said,
Pascal Robert 4.1 163
Pascal Robert 12.1 164 * save periodically
165 * don't retain references to objects you have inserted
166 * if that does not work, try creating a new EC periodically
167 * a bit heavy handed, but ec.invalidateAllObjects() should dump out all the snapshots as well.
Pascal Robert 4.1 168
Pascal Robert 12.1 169 A couple of useful comments:
Denis Frolov 15.1 170 [[http:~~/~~/lists.apple.com/archives/webobjects-dev/2004/Sep/msg00225.html>>url:http://lists.apple.com/archives/webobjects-dev/2004/Sep/msg00225.html||shape="rect"]]
171 [[http:~~/~~/lists.apple.com/archives/webobjects-dev/2004/Sep/msg00228.html>>url:http://lists.apple.com/archives/webobjects-dev/2004/Sep/msg00228.html||shape="rect"]]
smmccraw 10.1 172
Pascal Robert 12.1 173 It is always interesting to read the EOF docs, the JavaDocs are for API only while the EOF docs contain much valuable information.
Denis Frolov 15.1 174 [[http:~~/~~/developer.apple.com/documentation/WebObjects/Enterprise_Objects/index.html>>url:http://developer.apple.com/documentation/WebObjects/Enterprise_Objects/index.html||shape="rect"]]
smmccraw 10.1 175
Denis Frolov 15.1 176 "EOEditingContexts use weak references to the EOEnterpriseObjects registered with them. ... EOEditingContexts hold all inserted, deleted, or updated objects by strong references. These strong references are cleared by the EOEditingContext methods saveChanges ..." - [[http:~~/~~/developer.apple.com/documentation/WebObjects/Enterprise_Objects/Managing/chapter_7_section_9.html>>url:http://developer.apple.com/documentation/WebObjects/Enterprise_Objects/Managing/chapter_7_section_9.html||shape="rect"]]
smmccraw 10.1 177
Pascal Robert 12.1 178 === Alan Ward ===
Pascal Robert 4.1 179
Pascal Robert 12.1 180 Re: creating a new EC periodically I'd jump straight to this option (based on my experience). I have done a couple of similar tasks and found that the best way to ensure that your app doesn't balloon up is to periodically ditch the EC and create a new one. Seems like it shouldn't be necessary.... but it works.
Pascal Robert 4.1 181
Pascal Robert 12.1 182 === Ken Anderson ===
Pascal Robert 4.1 183
Pascal Robert 12.1 184 My guess is the memory increase is due to snapshots, which, as I understand it, will never go away, and wouldn't be considered a bug (to me anyway).
Denis Frolov 15.1 185 For dealing with a forever-import problem I had, where I didn't want the strong handed 'new EC' plan, I wrote this method, which keeps the snapshots down:
Pascal Robert 4.1 186
Pascal Robert 12.1 187 {{code}}
Pascal Robert 4.1 188
Pascal Robert 12.1 189 public static void forgetObjectsAndKeypaths(EOEditingContext ec, NSArray eos, NSArray keypaths) {
190 Enumeration eoEnum = eos.objectEnumerator();
191 while (eoEnum.hasMoreElements()) {
192 EOGenericRecord eo = (EOGenericRecord) eoEnum.nextElement();
193 Enumeration keypathEnum = keypaths.objectEnumerator();
194 while (keypathEnum.hasMoreElements()) {
195 String keypath = (String) keypathEnum.nextElement();
196 EOFaulting faulting = (EOFaulting) eo.valueForKeyPath(keypath);
197 if (faulting != null && !faulting.isFault()) {
198 if (eo.isToManyKey(keypath)) {
199 NSArray relEos = (NSArray) eo.valueForKeyPath(keypath);
200 EOHelper.forgetObjects(ec, relEos);
201 } else {
202 ec.forgetObject((EOCustomObject) eo.valueForKeyPath(keypath));
203 }
204 }
205 }
206 ec.forgetObject(eo);
207 }
208 }
Pascal Robert 4.1 209
Pascal Robert 12.1 210 {{/code}}
Pascal Robert 4.1 211
Pascal Robert 12.1 212 You can use it like this:
Pascal Robert 4.1 213
Pascal Robert 12.1 214 {{code}}
Pascal Robert 4.1 215
Pascal Robert 12.1 216 EOHelper.forgetObjectsAndKeypaths(ec, arrayOfEOsToForget, new NSArray(new String[]{"rel1", "rel2"});
Pascal Robert 4.1 217
Pascal Robert 12.1 218 {{/code}}
Pascal Robert 4.1 219
Denis Frolov 15.1 220 Of course, all the EOs in the array need to be the same entity for keypaths to work!