001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: MemoryBudget.java,v 1.54.2.10 2008/01/07 15:14:09 cwl Exp $
007: */
008:
009: package com.sleepycat.je.dbi;
010:
011: import java.util.Iterator;
012:
013: import com.sleepycat.je.DatabaseException;
014: import com.sleepycat.je.EnvironmentStats;
015: import com.sleepycat.je.StatsConfig;
016: import com.sleepycat.je.config.EnvironmentParams;
017: import com.sleepycat.je.latch.LatchSupport;
018: import com.sleepycat.je.tree.BIN;
019: import com.sleepycat.je.tree.DBIN;
020: import com.sleepycat.je.tree.DIN;
021: import com.sleepycat.je.tree.IN;
022:
023: /**
024: * MemoryBudget calculates the available memory for JE and how to apportion
025: * it between cache and log buffers. It is meant to centralize all memory
026: * calculations. Objects that ask for memory budgets should get settings from
027: * this class, rather than using the configuration parameter values directly.
028: */
029: public class MemoryBudget implements EnvConfigObserver {
030:
031: /*
032: * Object overheads. These are set statically with advance measurements.
033: * Java doesn't provide a way of assessing object size dynamically. These
034: * overheads will not be precise, but are close enough to let the system
035: * behave predictably.
036: *
037: * _32 values are the same on Windows and Solaris.
038: * _64 values are from 1.5.0_05 on Solaris.
039: * _14 values are from 1.4.2 on Windows and Solaris.
040: * _15 values are from 1.5.0_05 on Solaris and Windows.
041: *
042: * Specifically:
043: *
044: * java.vm.version=1.5.0_05_b05 os.name=SunOS
045: * java.vm.version=1.4.2_05_b04 os.name=SunOS
046: * java.vm.version=1.5.0_04_b05, os.name=Windows XP
047: * java.vm.version=1.4.2_06-b03, os.name=Windows XP
048: *
049: * The integer following the // below is the Sizeof argument used to
050: * compute the value.
051: */
052:
053: // 7
054: private final static int LONG_OVERHEAD_32 = 16;
055: private final static int LONG_OVERHEAD_64 = 24;
056:
057: // 8
058: private final static int ARRAY_OVERHEAD_32 = 16;
059: private final static int ARRAY_OVERHEAD_64 = 24;
060:
061: private final static int ARRAY_SIZE_INCLUDED_32 = 4;
062: private final static int ARRAY_SIZE_INCLUDED_64 = 0;
063:
064: // 2
065: private final static int OBJECT_OVERHEAD_32 = 8;
066: private final static int OBJECT_OVERHEAD_64 = 16;
067:
068: // (4 - ARRAY_OVERHEAD) / 256
069: // 64b: 4 is 2072
070: private final static int OBJECT_ARRAY_ITEM_OVERHEAD_32 = 4;
071: private final static int OBJECT_ARRAY_ITEM_OVERHEAD_64 = 8;
072:
073: // 20
074: private final static int HASHMAP_OVERHEAD_32 = 120;
075: private final static int HASHMAP_OVERHEAD_64 = 216;
076:
077: // 21 - OBJECT_OVERHEAD - HASHMAP_OVERHEAD
078: // 64b: 21 is max(280,...,287) on Linux/Solaris 1.5/1.6
079: private final static int HASHMAP_ENTRY_OVERHEAD_32 = 24;
080: private final static int HASHMAP_ENTRY_OVERHEAD_64 = 55;
081:
082: // 22
083: private final static int HASHSET_OVERHEAD_32 = 136;
084: private final static int HASHSET_OVERHEAD_64 = 240;
085:
086: // 23 - OBJECT_OVERHEAD - HASHSET_OVERHEAD
087: // 64b: 23 is max(304,...,311) on Linux/Solaris
088: private final static int HASHSET_ENTRY_OVERHEAD_32 = 24;
089: private final static int HASHSET_ENTRY_OVERHEAD_64 = 55;
090:
091: // HASHMAP_OVERHEAD * 2
092: private final static int TWOHASHMAPS_OVERHEAD_32 = 240;
093: private final static int TWOHASHMAPS_OVERHEAD_64 = 432;
094:
095: // 34
096: private final static int TREEMAP_OVERHEAD_32 = 40;
097: private final static int TREEMAP_OVERHEAD_64_15 = 64;
098: private final static int TREEMAP_OVERHEAD_64_16 = 80;
099:
100: // 35 - OBJECT_OVERHEAD - TREEMAP_OVERHEAD
101: // 64b: 35 is 144 on 1.5 and 160 on 1.6, result is 64 for both
102: private final static int TREEMAP_ENTRY_OVERHEAD_32 = 32;
103: private final static int TREEMAP_ENTRY_OVERHEAD_64 = 64;
104:
105: // 36
106: // 64b: 36 is 800 on 1.5 and max(840,853) on Linux/Solaris 1.6
107: private final static int MAPLN_OVERHEAD_32 = 464;
108: private final static int MAPLN_OVERHEAD_64_15 = 800;
109: private final static int MAPLN_OVERHEAD_64_16 = 853;
110:
111: // 9
112: private final static int LN_OVERHEAD_32 = 24;
113: private final static int LN_OVERHEAD_64 = 40;
114:
115: // 19
116: private final static int DUPCOUNTLN_OVERHEAD_32 = 24;
117: private final static int DUPCOUNTLN_OVERHEAD_64 = 48;
118:
119: // 12
120: private final static int BIN_FIXED_OVERHEAD_32_14 = 344;
121: private final static int BIN_FIXED_OVERHEAD_32_15 = 360;
122: private final static int BIN_FIXED_OVERHEAD_64_15 = 528;
123: private final static int BIN_FIXED_OVERHEAD_64_16 = 568;
124:
125: // 18
126: private final static int DIN_FIXED_OVERHEAD_32_14 = 352;
127: private final static int DIN_FIXED_OVERHEAD_32_15 = 360;
128: private final static int DIN_FIXED_OVERHEAD_64_15 = 536;
129: private final static int DIN_FIXED_OVERHEAD_64_16 = 576;
130:
131: // 17
132: private final static int DBIN_FIXED_OVERHEAD_32_14 = 352;
133: private final static int DBIN_FIXED_OVERHEAD_32_15 = 368;
134: private final static int DBIN_FIXED_OVERHEAD_64_15 = 544;
135: private final static int DBIN_FIXED_OVERHEAD_64_16 = 584;
136:
137: // 13
138: private final static int IN_FIXED_OVERHEAD_32_14 = 312;
139: private final static int IN_FIXED_OVERHEAD_32_15 = 320;
140: private final static int IN_FIXED_OVERHEAD_64_15 = 472;
141: private final static int IN_FIXED_OVERHEAD_64_16 = 512;
142:
143: // 6
144: private final static int KEY_OVERHEAD_32 = 16;
145: private final static int KEY_OVERHEAD_64 = 24;
146:
147: // 24
148: private final static int LOCK_OVERHEAD_32 = 24;
149: private final static int LOCK_OVERHEAD_64 = 48;
150:
151: // 25
152: private final static int LOCKINFO_OVERHEAD_32 = 16;
153: private final static int LOCKINFO_OVERHEAD_64 = 32;
154:
155: // 37
156: private final static int WRITE_LOCKINFO_OVERHEAD_32 = 24;
157: private final static int WRITE_LOCKINFO_OVERHEAD_64 = 32;
158:
159: /*
160: * Txn memory is the size for the Txn + a hashmap entry
161: * overhead for being part of the transaction table.
162: */
163: // 15
164: private final static int TXN_OVERHEAD_32_14 = 167;
165: private final static int TXN_OVERHEAD_32_15 = 175;
166: private final static int TXN_OVERHEAD_64 = 253;
167:
168: // 26
169: private final static int CHECKPOINT_REFERENCE_SIZE_32_14 = 32 + HASHSET_ENTRY_OVERHEAD_32;
170: private final static int CHECKPOINT_REFERENCE_SIZE_32_15 = 40 + HASHSET_ENTRY_OVERHEAD_32;
171: private final static int CHECKPOINT_REFERENCE_SIZE_64 = 56 + HASHSET_ENTRY_OVERHEAD_64;
172:
173: /* The per-log-file bytes used in UtilizationProfile. */
174: // 29 / 500
175: private final static int UTILIZATION_PROFILE_ENTRY_32 = 96;
176: private final static int UTILIZATION_PROFILE_ENTRY_64 = 144;
177:
178: /* Tracked File Summary overheads. */
179: // 31
180: private final static int TFS_LIST_INITIAL_OVERHEAD_32 = 464;
181: private final static int TFS_LIST_INITIAL_OVERHEAD_64 = 504;
182:
183: // 30
184: private final static int TFS_LIST_SEGMENT_OVERHEAD_32 = 440;
185: private final static int TFS_LIST_SEGMENT_OVERHEAD_64 = 464;
186:
187: // 33
188: private final static int LN_INFO_OVERHEAD_32 = 24;
189: private final static int LN_INFO_OVERHEAD_64 = 48;
190:
191: // 38
192: private final static int FILESUMMARYLN_OVERHEAD_32 = 104;
193: private final static int FILESUMMARYLN_OVERHEAD_64 = 160;
194:
195: /* Approximate element size in an ArrayList of Long. */
196: // (28 - 27) / 100
197: private final static int LONG_LIST_PER_ITEM_OVERHEAD_32 = 20;
198: private final static int LONG_LIST_PER_ITEM_OVERHEAD_64 = 32;
199:
200: public final static int LONG_OVERHEAD;
201: public final static int ARRAY_OVERHEAD;
202: public final static int ARRAY_SIZE_INCLUDED;
203: public final static int OBJECT_OVERHEAD;
204: public final static int OBJECT_ARRAY_ITEM_OVERHEAD;
205: public final static int HASHMAP_OVERHEAD;
206: public final static int HASHMAP_ENTRY_OVERHEAD;
207: public final static int HASHSET_OVERHEAD;
208: public final static int HASHSET_ENTRY_OVERHEAD;
209: public final static int TWOHASHMAPS_OVERHEAD;
210: public final static int TREEMAP_OVERHEAD;
211: public final static int TREEMAP_ENTRY_OVERHEAD;
212: public final static int MAPLN_OVERHEAD;
213: public final static int LN_OVERHEAD;
214: public final static int DUPCOUNTLN_OVERHEAD;
215: public final static int BIN_FIXED_OVERHEAD;
216: public final static int DIN_FIXED_OVERHEAD;
217: public final static int DBIN_FIXED_OVERHEAD;
218: public final static int IN_FIXED_OVERHEAD;
219: public final static int KEY_OVERHEAD;
220: public final static int LOCK_OVERHEAD;
221: public final static int LOCKINFO_OVERHEAD;
222: public final static int WRITE_LOCKINFO_OVERHEAD;
223: public final static int TXN_OVERHEAD;
224: public final static int CHECKPOINT_REFERENCE_SIZE;
225: public final static int UTILIZATION_PROFILE_ENTRY;
226: public final static int TFS_LIST_INITIAL_OVERHEAD;
227: public final static int TFS_LIST_SEGMENT_OVERHEAD;
228: public final static int LN_INFO_OVERHEAD;
229: public final static int FILESUMMARYLN_OVERHEAD;
230: public final static int LONG_LIST_PER_ITEM_OVERHEAD;
231:
232: /* Primitive long array item size is the same on all platforms. */
233: public final static int PRIMITIVE_LONG_ARRAY_ITEM_OVERHEAD = 8;
234:
235: private final static String JVM_ARCH_PROPERTY = "sun.arch.data.model";
236: private final static String FORCE_JVM_ARCH = "je.forceJVMArch";
237:
238: static {
239: String javaVersion = System.getProperty("java.version");
240: boolean isJVM15 = javaVersion != null
241: && javaVersion.startsWith("1.5.");
242: boolean isJVM14 = (LatchSupport.getJava5LatchClass() == null);
243:
244: boolean is64 = false;
245: String overrideArch = System.getProperty(FORCE_JVM_ARCH);
246: try {
247: if (overrideArch == null) {
248: String arch = System.getProperty(JVM_ARCH_PROPERTY);
249: if (arch != null) {
250: is64 = Integer.parseInt(arch) == 64;
251: }
252: } else {
253: is64 = Integer.parseInt(overrideArch) == 64;
254: }
255: } catch (NumberFormatException NFE) {
256: NFE.printStackTrace(System.err);
257: }
258:
259: if (is64) {
260: if (isJVM14) {
261: RuntimeException RE = new RuntimeException(
262: "1.4 based 64 bit JVM not supported");
263: RE.printStackTrace(System.err);
264: throw RE;
265: }
266: LONG_OVERHEAD = LONG_OVERHEAD_64;
267: ARRAY_OVERHEAD = ARRAY_OVERHEAD_64;
268: ARRAY_SIZE_INCLUDED = ARRAY_SIZE_INCLUDED_64;
269: OBJECT_OVERHEAD = OBJECT_OVERHEAD_64;
270: OBJECT_ARRAY_ITEM_OVERHEAD = OBJECT_ARRAY_ITEM_OVERHEAD_64;
271: HASHMAP_OVERHEAD = HASHMAP_OVERHEAD_64;
272: HASHMAP_ENTRY_OVERHEAD = HASHMAP_ENTRY_OVERHEAD_64;
273: HASHSET_OVERHEAD = HASHSET_OVERHEAD_64;
274: HASHSET_ENTRY_OVERHEAD = HASHSET_ENTRY_OVERHEAD_64;
275: TWOHASHMAPS_OVERHEAD = TWOHASHMAPS_OVERHEAD_64;
276: if (isJVM15) {
277: TREEMAP_OVERHEAD = TREEMAP_OVERHEAD_64_15;
278: MAPLN_OVERHEAD = MAPLN_OVERHEAD_64_15;
279: BIN_FIXED_OVERHEAD = BIN_FIXED_OVERHEAD_64_15;
280: DIN_FIXED_OVERHEAD = DIN_FIXED_OVERHEAD_64_15;
281: DBIN_FIXED_OVERHEAD = DBIN_FIXED_OVERHEAD_64_15;
282: IN_FIXED_OVERHEAD = IN_FIXED_OVERHEAD_64_15;
283: } else {
284: TREEMAP_OVERHEAD = TREEMAP_OVERHEAD_64_16;
285: MAPLN_OVERHEAD = MAPLN_OVERHEAD_64_16;
286: BIN_FIXED_OVERHEAD = BIN_FIXED_OVERHEAD_64_16;
287: DIN_FIXED_OVERHEAD = DIN_FIXED_OVERHEAD_64_16;
288: DBIN_FIXED_OVERHEAD = DBIN_FIXED_OVERHEAD_64_16;
289: IN_FIXED_OVERHEAD = IN_FIXED_OVERHEAD_64_16;
290: }
291: TREEMAP_ENTRY_OVERHEAD = TREEMAP_ENTRY_OVERHEAD_64;
292: LN_OVERHEAD = LN_OVERHEAD_64;
293: DUPCOUNTLN_OVERHEAD = DUPCOUNTLN_OVERHEAD_64;
294: TXN_OVERHEAD = TXN_OVERHEAD_64;
295: CHECKPOINT_REFERENCE_SIZE = CHECKPOINT_REFERENCE_SIZE_64;
296: KEY_OVERHEAD = KEY_OVERHEAD_64;
297: LOCK_OVERHEAD = LOCK_OVERHEAD_64;
298: LOCKINFO_OVERHEAD = LOCKINFO_OVERHEAD_64;
299: WRITE_LOCKINFO_OVERHEAD = WRITE_LOCKINFO_OVERHEAD_64;
300: UTILIZATION_PROFILE_ENTRY = UTILIZATION_PROFILE_ENTRY_64;
301: TFS_LIST_INITIAL_OVERHEAD = TFS_LIST_INITIAL_OVERHEAD_64;
302: TFS_LIST_SEGMENT_OVERHEAD = TFS_LIST_SEGMENT_OVERHEAD_64;
303: LN_INFO_OVERHEAD = LN_INFO_OVERHEAD_64;
304: FILESUMMARYLN_OVERHEAD = FILESUMMARYLN_OVERHEAD_64;
305: LONG_LIST_PER_ITEM_OVERHEAD = LONG_LIST_PER_ITEM_OVERHEAD_64;
306: } else {
307: LONG_OVERHEAD = LONG_OVERHEAD_32;
308: ARRAY_OVERHEAD = ARRAY_OVERHEAD_32;
309: ARRAY_SIZE_INCLUDED = ARRAY_SIZE_INCLUDED_32;
310: OBJECT_OVERHEAD = OBJECT_OVERHEAD_32;
311: OBJECT_ARRAY_ITEM_OVERHEAD = OBJECT_ARRAY_ITEM_OVERHEAD_32;
312: HASHMAP_OVERHEAD = HASHMAP_OVERHEAD_32;
313: HASHMAP_ENTRY_OVERHEAD = HASHMAP_ENTRY_OVERHEAD_32;
314: HASHSET_OVERHEAD = HASHSET_OVERHEAD_32;
315: HASHSET_ENTRY_OVERHEAD = HASHSET_ENTRY_OVERHEAD_32;
316: TWOHASHMAPS_OVERHEAD = TWOHASHMAPS_OVERHEAD_32;
317: TREEMAP_OVERHEAD = TREEMAP_OVERHEAD_32;
318: TREEMAP_ENTRY_OVERHEAD = TREEMAP_ENTRY_OVERHEAD_32;
319: MAPLN_OVERHEAD = MAPLN_OVERHEAD_32;
320: LN_OVERHEAD = LN_OVERHEAD_32;
321: DUPCOUNTLN_OVERHEAD = DUPCOUNTLN_OVERHEAD_32;
322: if (isJVM14) {
323: BIN_FIXED_OVERHEAD = BIN_FIXED_OVERHEAD_32_14;
324: DIN_FIXED_OVERHEAD = DIN_FIXED_OVERHEAD_32_14;
325: DBIN_FIXED_OVERHEAD = DBIN_FIXED_OVERHEAD_32_14;
326: IN_FIXED_OVERHEAD = IN_FIXED_OVERHEAD_32_14;
327: TXN_OVERHEAD = TXN_OVERHEAD_32_14;
328: CHECKPOINT_REFERENCE_SIZE = CHECKPOINT_REFERENCE_SIZE_32_14;
329: } else {
330: BIN_FIXED_OVERHEAD = BIN_FIXED_OVERHEAD_32_15;
331: DIN_FIXED_OVERHEAD = DIN_FIXED_OVERHEAD_32_15;
332: DBIN_FIXED_OVERHEAD = DBIN_FIXED_OVERHEAD_32_15;
333: IN_FIXED_OVERHEAD = IN_FIXED_OVERHEAD_32_15;
334: TXN_OVERHEAD = TXN_OVERHEAD_32_15;
335: CHECKPOINT_REFERENCE_SIZE = CHECKPOINT_REFERENCE_SIZE_32_15;
336: }
337: KEY_OVERHEAD = KEY_OVERHEAD_32;
338: LOCK_OVERHEAD = LOCK_OVERHEAD_32;
339: LOCKINFO_OVERHEAD = LOCKINFO_OVERHEAD_32;
340: WRITE_LOCKINFO_OVERHEAD = WRITE_LOCKINFO_OVERHEAD_32;
341: UTILIZATION_PROFILE_ENTRY = UTILIZATION_PROFILE_ENTRY_32;
342: TFS_LIST_INITIAL_OVERHEAD = TFS_LIST_INITIAL_OVERHEAD_32;
343: TFS_LIST_SEGMENT_OVERHEAD = TFS_LIST_SEGMENT_OVERHEAD_32;
344: LN_INFO_OVERHEAD = LN_INFO_OVERHEAD_32;
345: FILESUMMARYLN_OVERHEAD = FILESUMMARYLN_OVERHEAD_32;
346: LONG_LIST_PER_ITEM_OVERHEAD = LONG_LIST_PER_ITEM_OVERHEAD_32;
347: }
348: }
349:
350: /* public for unit tests. */
351: public final static long MIN_MAX_MEMORY_SIZE = 96 * 1024;
352: public final static String MIN_MAX_MEMORY_SIZE_STRING = Long
353: .toString(MIN_MAX_MEMORY_SIZE);
354:
355: /* This value prevents cache churn for apps with a high write rate. */
356: private final static int DEFAULT_MIN_BTREE_CACHE_SIZE = 500 * 1024;
357:
358: private final static long N_64MB = (1 << 26);
359:
360: /*
361: * Note that this class contains long fields that are accessed by multiple
362: * threads, and access to these fields is intentionally not synchronized.
363: * Although inaccuracies may result, correcting them is not worth the cost
364: * of synchronizing every time we adjust the treeMemoryUsage or
365: * miscMemoryUsage.
366: */
367:
368: /*
369: * Amount of memory cached for tree objects.
370: */
371: private long treeMemoryUsage;
372:
373: /*
374: * Amount of memory cached for Txn and other objects.
375: */
376: private long miscMemoryUsage;
377:
378: /*
379: * Used to protect treeMemoryUsage and miscMemoryUsage updates.
380: */
381: private Object memoryUsageSynchronizer = new Object();
382:
383: /*
384: * Number of lock tables (cache of EnvironmentParams.N_LOCK_TABLES).
385: */
386: private int nLockTables;
387:
388: /*
389: * Amount of memory cached for locks. Protected by the
390: * LockManager.lockTableLatches[lockTableIndex].
391: */
392: private long[] lockMemoryUsage;
393:
394: /*
395: * Memory available to JE, based on je.maxMemory and the memory available
396: * to this process.
397: */
398: private long maxMemory;
399: private long criticalThreshold; // experimental mark for sync eviction.
400:
401: /* Memory available to log buffers. */
402: private long logBufferBudget;
403:
404: /* Maximum allowed use of the misc budget by the UtilizationTracker. */
405: private long trackerBudget;
406:
407: /*
408: * Memory to hold internal nodes and misc memory (locks), controlled by the
409: * evictor. Does not include the log buffers.
410: */
411: private long cacheBudget;
412:
413: /* Mininum to prevent cache churn. */
414: private long minTreeMemoryUsage;
415:
416: /*
417: * Overheads that are a function of node capacity.
418: */
419: private long inOverhead;
420: private long binOverhead;
421: private long dinOverhead;
422: private long dbinOverhead;
423:
424: private EnvironmentImpl envImpl;
425:
426: MemoryBudget(EnvironmentImpl envImpl, DbConfigManager configManager)
427: throws DatabaseException {
428:
429: this .envImpl = envImpl;
430:
431: /* Request notification of mutable property changes. */
432: envImpl.addConfigObserver(this );
433:
434: /* Peform first time budget initialization. */
435: reset(configManager, true);
436:
437: /*
438: * Calculate IN and BIN overheads, which are a function of
439: * capacity. These values are stored in this class so that they can be
440: * calculated once per environment. The logic to do the calculations is
441: * left in the respective node classes so it can be done properly in
442: * the domain of those objects.
443: */
444: inOverhead = IN.computeOverhead(configManager);
445: binOverhead = BIN.computeOverhead(configManager);
446: dinOverhead = DIN.computeOverhead(configManager);
447: dbinOverhead = DBIN.computeOverhead(configManager);
448: }
449:
450: /**
451: * Respond to config updates.
452: */
453: public void envConfigUpdate(DbConfigManager configManager)
454: throws DatabaseException {
455:
456: /*
457: * Reinitialize the cache budget and the log buffer pool, in that
458: * order. Do not reset the log buffer pool if the log buffer budget
459: * hasn't changed, since that is expensive and may cause I/O.
460: */
461: long oldLogBufferBudget = logBufferBudget;
462: reset(configManager, false);
463: if (oldLogBufferBudget != logBufferBudget) {
464: envImpl.getLogManager().resetPool(configManager);
465: }
466: }
467:
468: /**
469: * Initialize at construction time and when the cache is resized.
470: */
471: private void reset(DbConfigManager configManager,
472: boolean resetLockMemoryUsage) throws DatabaseException {
473:
474: /*
475: * Calculate the total memory allotted to JE.
476: * 1. If je.maxMemory is specified, use that. Check that it's
477: * not more than the jvm memory.
478: * 2. Otherwise, take je.maxMemoryPercent * JVM max memory.
479: */
480: long newMaxMemory = configManager
481: .getLong(EnvironmentParams.MAX_MEMORY);
482: long jvmMemory = getRuntimeMaxMemory();
483:
484: if (newMaxMemory != 0) {
485: /* Application specified a cache size number, validate it. */
486: if (jvmMemory < newMaxMemory) {
487: throw new IllegalArgumentException(
488: EnvironmentParams.MAX_MEMORY.getName()
489: + " has a value of "
490: + newMaxMemory
491: + " but the JVM is only configured for "
492: + jvmMemory
493: + ". Consider using je.maxMemoryPercent.");
494: }
495: if (newMaxMemory < MIN_MAX_MEMORY_SIZE) {
496: throw new IllegalArgumentException(
497: EnvironmentParams.MAX_MEMORY.getName() + " is "
498: + newMaxMemory
499: + " which is less than the minimum: "
500: + MIN_MAX_MEMORY_SIZE);
501: }
502: } else {
503:
504: /*
505: * When no explicit cache size is specified and the JVM memory size
506: * is unknown, assume a default sized (64 MB) heap. This produces
507: * a reasonable cache size when no heap size is known.
508: */
509: if (jvmMemory == Long.MAX_VALUE) {
510: jvmMemory = N_64MB;
511: }
512:
513: /* Use the configured percentage of the JVM memory size. */
514: int maxMemoryPercent = configManager
515: .getInt(EnvironmentParams.MAX_MEMORY_PERCENT);
516: newMaxMemory = (maxMemoryPercent * jvmMemory) / 100;
517: }
518:
519: /*
520: * Calculate the memory budget for log buffering. If the LOG_MEM_SIZE
521: * parameter is not set, start by using 7% (1/16th) of the cache
522: * size. If it is set, use that explicit setting.
523: *
524: * No point in having more log buffers than the maximum size. If
525: * this starting point results in overly large log buffers,
526: * reduce the log buffer budget again.
527: */
528: long newLogBufferBudget = configManager
529: .getLong(EnvironmentParams.LOG_MEM_SIZE);
530: if (newLogBufferBudget == 0) {
531: newLogBufferBudget = newMaxMemory >> 4;
532: } else if (newLogBufferBudget > newMaxMemory / 2) {
533: newLogBufferBudget = newMaxMemory / 2;
534: }
535:
536: /*
537: * We have a first pass at the log buffer budget. See what
538: * size log buffers result. Don't let them be too big, it would
539: * be a waste.
540: */
541: int numBuffers = configManager
542: .getInt(EnvironmentParams.NUM_LOG_BUFFERS);
543: long startingBufferSize = newLogBufferBudget / numBuffers;
544: int logBufferSize = configManager
545: .getInt(EnvironmentParams.LOG_BUFFER_MAX_SIZE);
546: if (startingBufferSize > logBufferSize) {
547: startingBufferSize = logBufferSize;
548: newLogBufferBudget = numBuffers * startingBufferSize;
549: } else if (startingBufferSize < EnvironmentParams.MIN_LOG_BUFFER_SIZE) {
550: startingBufferSize = EnvironmentParams.MIN_LOG_BUFFER_SIZE;
551: newLogBufferBudget = numBuffers * startingBufferSize;
552: }
553:
554: long newCriticalThreshold = (newMaxMemory * envImpl
555: .getConfigManager().getInt(
556: EnvironmentParams.EVICTOR_CRITICAL_PERCENTAGE)) / 100;
557:
558: long newTrackerBudget = (newMaxMemory * envImpl
559: .getConfigManager()
560: .getInt(
561: EnvironmentParams.CLEANER_DETAIL_MAX_MEMORY_PERCENTAGE)) / 100;
562:
563: long newMinTreeMemoryUsage = configManager
564: .getLong(EnvironmentParams.MIN_TREE_MEMORY);
565:
566: /*
567: * If all has gone well, update the budget fields. Once the log buffer
568: * budget is determined, the remainder of the memory is left for tree
569: * nodes.
570: */
571: maxMemory = newMaxMemory;
572: criticalThreshold = newCriticalThreshold;
573: logBufferBudget = newLogBufferBudget;
574: trackerBudget = newTrackerBudget;
575: cacheBudget = newMaxMemory - newLogBufferBudget;
576: minTreeMemoryUsage = Math.min(newMinTreeMemoryUsage,
577: cacheBudget);
578: if (resetLockMemoryUsage) {
579: nLockTables = configManager
580: .getInt(EnvironmentParams.N_LOCK_TABLES);
581: lockMemoryUsage = new long[nLockTables];
582: }
583: }
584:
585: /**
586: * Returns Runtime.maxMemory(), accounting for a MacOS bug.
587: * May return Long.MAX_VALUE if there is no inherent limit.
588: * Used by unit tests as well as by this class.
589: */
590: public static long getRuntimeMaxMemory() {
591:
592: /* Runtime.maxMemory is unreliable on MacOS Java 1.4.2. */
593: if ("Mac OS X".equals(System.getProperty("os.name"))) {
594: String jvmVersion = System.getProperty("java.version");
595: if (jvmVersion != null && jvmVersion.startsWith("1.4.2")) {
596: return Long.MAX_VALUE; /* Undetermined heap size. */
597: }
598: }
599:
600: return Runtime.getRuntime().maxMemory();
601: }
602:
603: /**
604: * Initialize the starting environment memory state.
605: */
606: void initCacheMemoryUsage() throws DatabaseException {
607:
608: /*
609: * The memoryUsageSynchronizer mutex is at the bottom of the lock
610: * hierarchy and should always be taken last. Since
611: * calcTreeCacheUsage() takes the INList latch, we get the usage value
612: * outside the mutex and then assign the value, in order to preserve
613: * correct lock hierarchy. That said, initCacheMemoryUsage should be
614: * called while the system is quiescent, and there should be no lock
615: * conflict possible even if the locks were taken in reverse. [#15364]
616: */
617: long calculatedUsage = calcTreeCacheUsage();
618: synchronized (memoryUsageSynchronizer) {
619: treeMemoryUsage = calculatedUsage;
620: }
621: assert LatchSupport.countLatchesHeld() == 0;
622: }
623:
624: /**
625: * Public for testing.
626: */
627: public long calcTreeCacheUsage() throws DatabaseException {
628:
629: long totalSize = 0;
630: INList inList = envImpl.getInMemoryINs();
631:
632: inList.latchMajor();
633: try {
634: Iterator iter = inList.iterator();
635: while (iter.hasNext()) {
636: IN in = (IN) iter.next();
637: long size = in.getInMemorySize();
638: totalSize += size;
639: }
640: } finally {
641: inList.releaseMajorLatch();
642: }
643: return totalSize;
644: }
645:
646: /**
647: * Returns whether eviction of INList information is allowed.
648: * To prevent extreme cache churn, eviction of Btree information is
649: * prohibited unless the tree memory usage is above this minimum value.
650: */
651: public boolean isTreeUsageAboveMinimum() {
652: return treeMemoryUsage > minTreeMemoryUsage;
653: }
654:
655: /**
656: * For unit tests.
657: */
658: public long getMinTreeMemoryUsage() {
659: return minTreeMemoryUsage;
660: }
661:
662: /**
663: * Update the environment wide tree memory count, wake up the evictor if
664: * necessary.
665: * @param increment note that increment may be negative.
666: */
667: public void updateTreeMemoryUsage(long increment) {
668: synchronized (memoryUsageSynchronizer) {
669: treeMemoryUsage += increment;
670: }
671: if (getCacheMemoryUsage() > cacheBudget) {
672: envImpl.alertEvictor();
673: }
674: }
675:
676: /**
677: * Update the environment wide misc memory count, wake up the evictor if
678: * necessary.
679: * @param increment note that increment may be negative.
680: */
681: public void updateMiscMemoryUsage(long increment) {
682: synchronized (memoryUsageSynchronizer) {
683: miscMemoryUsage += increment;
684: }
685: if (getCacheMemoryUsage() > cacheBudget) {
686: envImpl.alertEvictor();
687: }
688: }
689:
690: public void updateLockMemoryUsage(long increment, int lockTableIndex) {
691: lockMemoryUsage[lockTableIndex] += increment;
692: if (getCacheMemoryUsage() > cacheBudget) {
693: envImpl.alertEvictor();
694: }
695: }
696:
697: public long accumulateNewUsage(IN in, long newSize) {
698: return in.getInMemorySize() + newSize;
699: }
700:
701: public void refreshTreeMemoryUsage(long newSize) {
702: synchronized (memoryUsageSynchronizer) {
703: treeMemoryUsage = newSize;
704: }
705: }
706:
707: public long getCacheMemoryUsage() {
708: long accLockMemoryUsage = accumulateLockUsage();
709:
710: return treeMemoryUsage + miscMemoryUsage + accLockMemoryUsage;
711: }
712:
713: private long accumulateLockUsage() {
714: long accLockMemoryUsage = 0;
715: if (nLockTables == 1) {
716: accLockMemoryUsage = lockMemoryUsage[0];
717: } else {
718: for (int i = 0; i < nLockTables; i++) {
719: accLockMemoryUsage += lockMemoryUsage[i];
720: }
721: }
722: return accLockMemoryUsage;
723: }
724:
725: /**
726: * Used for unit testing.
727: */
728: public long getTreeMemoryUsage() {
729: return treeMemoryUsage;
730: }
731:
732: /**
733: * Used for unit testing.
734: */
735: public long getMiscMemoryUsage() {
736: return miscMemoryUsage;
737: }
738:
739: public long getLogBufferBudget() {
740: return logBufferBudget;
741: }
742:
743: public long getTrackerBudget() {
744: return trackerBudget;
745: }
746:
747: public long getMaxMemory() {
748: return maxMemory;
749: }
750:
751: public long getCriticalThreshold() {
752: return criticalThreshold;
753: }
754:
755: public long getCacheBudget() {
756: return cacheBudget;
757: }
758:
759: public long getINOverhead() {
760: return inOverhead;
761: }
762:
763: public long getBINOverhead() {
764: return binOverhead;
765: }
766:
767: public long getDINOverhead() {
768: return dinOverhead;
769: }
770:
771: public long getDBINOverhead() {
772: return dbinOverhead;
773: }
774:
775: /**
776: * Returns the memory size occupied by a byte array of a given length. All
777: * arrays (regardless of element type) have the same overhead for a zero
778: * length array. On 32b Java, there are 4 bytes included in that fixed
779: * overhead that can be used for the first N elements -- however many fit
780: * in 4 bytes. On 64b Java, there is no extra space included. In all
781: * cases, space is allocated in 8 byte chunks.
782: */
783: public static int byteArraySize(int arrayLen) {
784:
785: /*
786: * ARRAY_OVERHEAD accounts for N bytes of data, which is 4 bytes on 32b
787: * Java and 0 bytes on 64b Java. Data larger than N bytes is allocated
788: * in 8 byte increments.
789: */
790: int size = ARRAY_OVERHEAD;
791: if (arrayLen > ARRAY_SIZE_INCLUDED) {
792: size += ((arrayLen - ARRAY_SIZE_INCLUDED + 7) / 8) * 8;
793: }
794:
795: return size;
796: }
797:
798: public static int shortArraySize(int arrayLen) {
799: return byteArraySize(arrayLen * 2);
800: }
801:
802: public static int intArraySize(int arrayLen) {
803: return byteArraySize(arrayLen * 4);
804: }
805:
806: public static int objectArraySize(int arrayLen) {
807: return byteArraySize(arrayLen * OBJECT_ARRAY_ITEM_OVERHEAD);
808: }
809:
810: void loadStats(StatsConfig config, EnvironmentStats stats) {
811: stats.setCacheDataBytes(getCacheMemoryUsage());
812: stats.setAdminBytes(miscMemoryUsage);
813: stats.setLockBytes(accumulateLockUsage());
814: }
815: }
|