slang@bmerh563.bnr.ca (Steven Langlois) (04/24/91)
I have some questions about memory management with large applications. Unfortunately, most sample code from any source is not nearly large enough to encounter memory management issues. The application I have been working on for some time now is approaching 375K. I have set the SIZE resource to specify an application memory size of 512K. Far too much of the available 512K is used up by code segments. Now I know about UnloadSeg and intend to use it but this is where my questions come into play. What is the best method for unloading segments? There is the "unload all your segments every time through your event loop" method but is this an effective method or are there other better methods? From what I have read, it is typically a good idea to unload your initialization segment before entering your main event loop. You will then not have to unload the this segment again because you know that you will never call the initialization code again. After doing this should you compact the heap or wait until a call to the memory manager does it for you? Should your application periodically compact the heap to keep it from getting fragmented? If so, when is a good time, maybe after the user executes a menu command? For those knowledgeable in THINK Pascal, is it a good item to load and lock the runtime environment or should you allow it to be unloaded like any other segment? What would happen if there was not enough memory to load a CODE segment? Would the program crash or just not call the routine in the segment? And more importantly, how do you prevent this from happening, especially when calling the routine may cause a chain of segments to be loaded? Well, I guess that will do for now. Any help would be greatly appreciated. Steven Langlois ISDN Basic Rate Access BNR, a subsidiary of Northern Telecom Bitnet: slang@bnr.ca Bitnet from AppleLink: slang@bnr.ca@DASNET#
dorner@pequod.cso.uiuc.edu (Steve Dorner) (04/25/91)
>There is the "unload all your segments every time through your event loop" >effective method or are there other better methods? If you know your code in and out, you could unload segments on the fly. Just be darn careful you don't unload a segment that holds anything on the current call stack. You're PROBABLY better off not doing this, unless you have a very special program, since it will require you to do a lot of careful bookkeeping on what routines call what and what segments they're in, and will probably not gain you much anyway. >Should your application periodically compact the heap to keep it from >getting fragmented? No. Won't do you any good. Just make sure you don't have relocatable blocks lying around and you'll be fine. >What would happen if there was not enough memory to load a CODE segment? La Bombe. >And more importantly, how do you prevent >this from happening There are a couple of ways. You can reserve a goodly chunk of memory, and release it in your GrowZone procedure; this will cover your posterior often. The other way is to have intimate knowledge of inter-segment references, and always preflight such calls by checking if there is enough memory to load the needed segment. I think MacApp does this. I doubt it's practical for the average developer, unless you come up with an elaborate segmentation system. if (EnoughMemoryForCodeSegment(32)) CallSomeFunction(withSomeArg); else RefusePolitely(); would get real old if it weren't being done automagically by your development environment. I'd suggest you get a copy of "How to Write Macintosh Software" by Scott Knaster. It's a big help with issues like this. -- Steve Dorner, U of Illinois Computing Services Office Internet: s-dorner@uiuc.edu UUCP: uunet!uiucuxc!uiuc.edu!s-dorner
dorner@pequod.cso.uiuc.edu (Steve Dorner) (04/25/91)
I mumbled: >No. Won't do you any good. Just make sure you don't have relocatable >blocks lying around and you'll be fine. I meant "non-relocatable", of course. -- Steve Dorner, U of Illinois Computing Services Office Internet: s-dorner@uiuc.edu UUCP: uunet!uiucuxc!uiuc.edu!s-dorner
pratt@boulder.Colorado.EDU (Jonathan Pratt) (04/25/91)
In article <1991Apr24.170354.20749@ux1.cso.uiuc.edu> dorner@pequod.cso.uiuc.edu (Steve Dorner) writes: > [Stuff about making sure it's safe to call another segment] > if (EnoughMemoryForCodeSegment(32)) CallSomeFunction(withSomeArg); > else RefusePolitely(); > I thought I'd share my version of EnoughMemoryForCodeSegment. I don't find it too inconvenient to use, and it's definitely preferable to giving the end user a system error. My typical usage is if (LoadCheck((short *) AFunction) { AFunction(...); UnloadSeg(AFunction); } All of my base code (libraries, utilities, etc.) is preloaded and locked, so the LoadCheck bit is used principally for isolated functions, although for safety I use it before dispatching to routines associated with menus, windows, etc. (which are known only by ProcPtr variables). No flames please about the in-line assembly. You have to have it to call LoadSeg (a trap THINK C doesn't seem to recognize). I've been assueming that in any case where LoadCheck succeeds, the call to the function won't scramble memory provided the function itself doesn't. Can anyone contradict this? short LoadCheck(theproc) register short *theproc; { /* This next line makes sure there is a LoadSeg call in the jump table * so that it's safe to extract the segment number to user with GetResource.*/ if (*theproc == 0x3f3c && *(theproc+2)== 0xa9f0) if (!GetResource('CODE',*(theproc+1)) ) { GiveTheError(-108); return false; } else asm { Move.W 2(theproc),-(A7) Bra.S @in Bra.S @cont NOP @in DC.W 0xa9f0 /* _LoadSeg */ @cont } return true; } An obvious improvement is to use ResError() instead of -108 in GiveTheError. That way you would accurately tell the user if it the failure was caused by a disk error rather than insufficient memory. /* Jonathan Pratt Internet: pratt@boulder.colorado.edu * * UCHSC, Box A034 uucp: ..!ncar!boulder!pratt * * 4200 E. Ninth Ave. * * Denver, CO 80262 Phone: (303) 270-7801 */
Lawson.English@p88.f15.n300.z1.fidonet.org (Lawson English) (05/01/91)
Steven Langlois writes in a message to All SL> For those knowledgeable in THINK Pascal, is it a good item to SL> load and lock the runtime environment or should you allow it SL> to be unloaded like any other segment? I can't answer any of the other questions, but, if you check, the runtime environment of Think Pascal should be specified as un-unloadable by default if you use one of the example programs to build on (at least mine is). Lawson -- Uucp: ...{gatech,ames,rutgers}!ncar!asuvax!stjhmc!300!15.88!Lawson.English Internet: Lawson.English@p88.f15.n300.z1.fidonet.org
lsr@Apple.COM (Larry Rosenstein) (05/03/91)
In article <1991Apr24.122618.13791@bmers95.bnr.ca> slang@bmerh563.bnr.ca (Steven Langlois) writes: >What is the best method for unloading segments? In MacApp we unload non-resident segments after each event. (The routine to do this can be called at any time, if you're careful.) Resident segment is a MacApp concept. In the simplest case, the app includes a resource listing the names of the resident segments. You can also mark segments as resident programmatically. >There is the "unload all your segments every time through your event loop" >method but is this an effective method or are there other better methods? Some people have proposed trying to figure out if a segment is being used by tracing down the call stack, but I'm not sure if this is any better. (I think the idea was to do this when you needed extra memory.) Unloading a segment doesn't purge it from memory; it simply marks the jump table entries as unloaded, and allows the segment to float in the heap. If you use it again before it gets purged, then it doesn't have to be read from disk. >From what I have read, it is typically a good idea to unload your >initialization segment before entering your main event loop. You will then You should be careful that unloading this segment doesn't create a hole in the heap because other segments were loaded after it and these weren't also unloaded. For example, you know that the main segment is loaded first and never gets unloaded. It can make a call to the initialization code, which will load various segments. Before starting the main event loop, you can unloaded all segments except the main segment, in order to start with a clean slate. It help to check the state of your heap at various times to make sure that there are no free block surrounded by locked code segments. >compact the heap or wait until a call to the memory manager does it for >you? It should never be necessary to call explicitly compact the heap, if there are no non-relocatable or locked blocks in the middle of the heap. You also have to be careful about a very large relocatable block that's bigger than any free space, because it is essentially locked and can't be moved. >What would happen if there was not enough memory to load a CODE segment? You get a system error. The same is true of failing to load other resources like defprocs, packages, ... >more importantly, how do you prevent this from happening, especially when As someone said you need to keep a reserve handle around that you can free if necessary. The Memory Manager allows you to install a grow zone proc that will be called if it needs more memory. You can release the handle in your grow zone. MacApp does essentially this except that we try to be clever about the size of this handle so that it doesn't take up memory needlessly. The concept is that your application needs X bytes of space for the maximum amount of code, defprocs, ... you use at any point in time. To prevent a crash, you have to make sure that you have this amount of memory available. Segments that are already in memory count towards satisfying this requirement. So the reserve handle size can be reduced by the total sizes of in-memory code segments. Another subtle point is that you only need to guarantee this reserve memory before you make a non-segment request. (In MacApp we call this a permanent request, because the memory is permanently allocated in the document, and can't be freed unless the user deletes something.) MacApp maintains a flag that indicates whether the program is in permanent or temporary request mode. When the flag is changed from temporary to permanent we make sure that the reserve handle is allocated properly. The grow zone proc also uses the flag to decide if the reserve should be freed. This scheme works very well as long as you tell MacApp the maximum amount of loaded code, defprocs, ... you need. The MacApp debugger has tools that help to figure this out, and the sizes are specified in resources. And you still need to check whether permanent allocations succeed, and gracefully back out if not. -- Larry Rosenstein, Apple Computer, Inc. lsr@apple.com (or AppleLink: Rosenstein1)
<CHARLESW@QUCDN.QueensU.CA> (05/14/91)
There is also a good article on MacApp's memory management in the Fall/89 "Dr. Dobb's Macintosh Journal" by Curt Bianchi. It applies to MacApp 2.0b9. .../Dave