Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
RamUsageEstimator.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using System.Collections.Generic;
20 
21 namespace Lucene.Net.Util
22 {
23 
24  /// <summary> Estimates the size of a given Object using a given MemoryModel for primitive
25  /// size information.
26  ///
27  /// Resource Usage:
28  ///
29  /// Internally uses a Map to temporally hold a reference to every
30  /// object seen.
31  ///
32  /// If checkIntered, all Strings checked will be interned, but those
33  /// that were not already interned will be released for GC when the
34  /// estimate is complete.
35  /// </summary>
36  public sealed class RamUsageEstimator
37  {
38  private MemoryModel memoryModel;
39 
40  private IDictionary<object, object> seen;
41 
42  private int refSize;
43  private int arraySize;
44  private int classSize;
45 
46  private bool checkInterned;
47 
48  /// <summary> Constructs this object with an AverageGuessMemoryModel and
49  /// checkInterned = true.
50  /// </summary>
52  {
53  }
54 
55  /// <param name="checkInterned">check if Strings are interned and don't add to size
56  /// if they are. Defaults to true but if you know the objects you are checking
57  /// won't likely contain many interned Strings, it will be faster to turn off
58  /// intern checking.
59  /// </param>
60  public RamUsageEstimator(bool checkInterned):this(new AverageGuessMemoryModel(), checkInterned)
61  {
62  }
63 
64  /// <param name="memoryModel">MemoryModel to use for primitive object sizes.
65  /// </param>
66  public RamUsageEstimator(MemoryModel memoryModel):this(memoryModel, true)
67  {
68  }
69 
70  /// <param name="memoryModel">MemoryModel to use for primitive object sizes.
71  /// </param>
72  /// <param name="checkInterned">check if Strings are interned and don't add to size
73  /// if they are. Defaults to true but if you know the objects you are checking
74  /// won't likely contain many interned Strings, it will be faster to turn off
75  /// intern checking.
76  /// </param>
77  public RamUsageEstimator(MemoryModel memoryModel, bool checkInterned)
78  {
79  this.memoryModel = memoryModel;
80  this.checkInterned = checkInterned;
81  // Use Map rather than Set so that we can use an IdentityHashMap - not
82  // seeing an IdentityHashSet
83  seen = new IdentityDictionary<object, object>(64);
84  this.refSize = memoryModel.ReferenceSize;
85  this.arraySize = memoryModel.ArraySize;
86  this.classSize = memoryModel.ClassSize;
87  }
88 
89  public long EstimateRamUsage(System.Object obj)
90  {
91  long size = Size(obj);
92  seen.Clear();
93  return size;
94  }
95 
96  private long Size(System.Object obj)
97  {
98  if (obj == null)
99  {
100  return 0;
101  }
102  // interned not part of this object
103  if (checkInterned && obj is System.String && obj == (System.Object) String.Intern(((System.String) obj)))
104  {
105  // interned string will be eligible
106  // for GC on
107  // estimateRamUsage(Object) return
108  return 0;
109  }
110 
111  // skip if we have seen before
112  if (seen.ContainsKey(obj))
113  {
114  return 0;
115  }
116 
117  // add to seen
118  seen[obj] = null;
119 
120  System.Type clazz = obj.GetType();
121  if (clazz.IsArray)
122  {
123  return SizeOfArray(obj);
124  }
125 
126  long size = 0;
127 
128  // walk type hierarchy
129  while (clazz != null)
130  {
131  System.Reflection.FieldInfo[] fields = clazz.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Static);
132  for (int i = 0; i < fields.Length; i++)
133  {
134  if (fields[i].IsStatic)
135  {
136  continue;
137  }
138 
139  if (fields[i].FieldType.IsPrimitive)
140  {
141  size += memoryModel.GetPrimitiveSize(fields[i].FieldType);
142  }
143  else
144  {
145  size += refSize;
146  fields[i].GetType();
147  try
148  {
149  System.Object value_Renamed = fields[i].GetValue(obj);
150  if (value_Renamed != null)
151  {
152  size += Size(value_Renamed);
153  }
154  }
155  catch (System.UnauthorizedAccessException)
156  {
157  // ignore for now?
158  }
159  }
160  }
161  clazz = clazz.BaseType;
162  }
163  size += classSize;
164  return size;
165  }
166 
167  private long SizeOfArray(System.Object obj)
168  {
169  int len = ((System.Array) obj).Length;
170  if (len == 0)
171  {
172  return 0;
173  }
174  long size = arraySize;
175  System.Type arrayElementClazz = obj.GetType().GetElementType();
176  if (arrayElementClazz.IsPrimitive)
177  {
178  size += len * memoryModel.GetPrimitiveSize(arrayElementClazz);
179  }
180  else
181  {
182  for (int i = 0; i < len; i++)
183  {
184  size += refSize + Size(((System.Array) obj).GetValue(i));
185  }
186  }
187 
188  return size;
189  }
190 
191  private const long ONE_KB = 1024;
192  private static readonly long ONE_MB = ONE_KB * ONE_KB;
193  private static readonly long ONE_GB = ONE_KB * ONE_MB;
194 
195  /// <summary> Return good default units based on byte size.</summary>
196  public static System.String HumanReadableUnits(long bytes, System.IFormatProvider df)
197  {
198  System.String newSizeAndUnits;
199 
200  if (bytes / ONE_GB > 0)
201  {
202  newSizeAndUnits = System.Convert.ToString(((float) bytes / ONE_GB), df) + " GB";
203  }
204  else if (bytes / ONE_MB > 0)
205  {
206  newSizeAndUnits = System.Convert.ToString((float) bytes / ONE_MB, df) + " MB";
207  }
208  else if (bytes / ONE_KB > 0)
209  {
210  newSizeAndUnits = System.Convert.ToString((float) bytes / ONE_KB, df) + " KB";
211  }
212  else
213  {
214  newSizeAndUnits = System.Convert.ToString(bytes) + " bytes";
215  }
216 
217  return newSizeAndUnits;
218  }
219  }
220 }