44 // this would have been to map the entire G1 heap to a single memory |
44 // this would have been to map the entire G1 heap to a single memory |
45 // pool. However, it's helpful to show how large the eden and survivor |
45 // pool. However, it's helpful to show how large the eden and survivor |
46 // get, as this does affect the performance and behavior of G1. Which |
46 // get, as this does affect the performance and behavior of G1. Which |
47 // is why we introduce the three memory pools implemented here. |
47 // is why we introduce the three memory pools implemented here. |
48 // |
48 // |
49 // The above approach inroduces a couple of challenging issues in the |
49 // See comments in g1MonitoringSupport.hpp for additional details |
50 // implementation of the three memory pools: |
50 // on this model. |
51 // |
51 // |
52 // 1) The used space calculation for a pool is not necessarily |
|
53 // independent of the others. We can easily get from G1 the overall |
|
54 // used space in the entire heap, the number of regions in the young |
|
55 // generation (includes both eden and survivors), and the number of |
|
56 // survivor regions. So, from that we calculate: |
|
57 // |
|
58 // survivor_used = survivor_num * region_size |
|
59 // eden_used = young_region_num * region_size - survivor_used |
|
60 // old_gen_used = overall_used - eden_used - survivor_used |
|
61 // |
|
62 // Note that survivor_used and eden_used are upper bounds. To get the |
|
63 // actual value we would have to iterate over the regions and add up |
|
64 // ->used(). But that'd be expensive. So, we'll accept some lack of |
|
65 // accuracy for those two. But, we have to be careful when calculating |
|
66 // old_gen_used, in case we subtract from overall_used more then the |
|
67 // actual number and our result goes negative. |
|
68 // |
|
69 // 2) Calculating the used space is straightforward, as described |
|
70 // above. However, how do we calculate the committed space, given that |
|
71 // we allocate space for the eden, survivor, and old gen out of the |
|
72 // same pool of regions? One way to do this is to use the used value |
|
73 // as also the committed value for the eden and survivor spaces and |
|
74 // then calculate the old gen committed space as follows: |
|
75 // |
|
76 // old_gen_committed = overall_committed - eden_committed - survivor_committed |
|
77 // |
|
78 // Maybe a better way to do that would be to calculate used for eden |
|
79 // and survivor as a sum of ->used() over their regions and then |
|
80 // calculate committed as region_num * region_size (i.e., what we use |
|
81 // to calculate the used space now). This is something to consider |
|
82 // in the future. |
|
83 // |
|
84 // 3) Another decision that is again not straightforward is what is |
|
85 // the max size that each memory pool can grow to. One way to do this |
|
86 // would be to use the committed size for the max for the eden and |
|
87 // survivors and calculate the old gen max as follows (basically, it's |
|
88 // a similar pattern to what we use for the committed space, as |
|
89 // described above): |
|
90 // |
|
91 // old_gen_max = overall_max - eden_max - survivor_max |
|
92 // |
|
93 // Unfortunately, the above makes the max of each pool fluctuate over |
|
94 // time and, even though this is allowed according to the spec, it |
|
95 // broke several assumptions in the M&M framework (there were cases |
|
96 // where used would reach a value greater than max). So, for max we |
|
97 // use -1, which means "undefined" according to the spec. |
|
98 // |
|
99 // 4) Now, there is a very subtle issue with all the above. The |
|
100 // framework will call get_memory_usage() on the three pools |
|
101 // asynchronously. As a result, each call might get a different value |
|
102 // for, say, survivor_num which will yield inconsistent values for |
|
103 // eden_used, survivor_used, and old_gen_used (as survivor_num is used |
|
104 // in the calculation of all three). This would normally be |
|
105 // ok. However, it's possible that this might cause the sum of |
|
106 // eden_used, survivor_used, and old_gen_used to go over the max heap |
|
107 // size and this seems to sometimes cause JConsole (and maybe other |
|
108 // clients) to get confused. There's not a really an easy / clean |
|
109 // solution to this problem, due to the asynchrounous nature of the |
|
110 // framework. |
|
111 |
52 |
112 |
53 |
113 // This class is shared by the three G1 memory pool classes |
54 // This class is shared by the three G1 memory pool classes |
114 // (G1EdenPool, G1SurvivorPool, G1OldGenPool). Given that the way we |
55 // (G1EdenPool, G1SurvivorPool, G1OldGenPool). Given that the way we |
115 // calculate used / committed bytes for these three pools is related |
56 // calculate used / committed bytes for these three pools is related |
116 // (see comment above), we put the calculations in this class so that |
57 // (see comment above), we put the calculations in this class so that |
117 // we can easily share them among the subclasses. |
58 // we can easily share them among the subclasses. |
118 class G1MemoryPoolSuper : public CollectedMemoryPool { |
59 class G1MemoryPoolSuper : public CollectedMemoryPool { |
119 private: |
|
120 // It returns x - y if x > y, 0 otherwise. |
|
121 // As described in the comment above, some of the inputs to the |
|
122 // calculations we have to do are obtained concurrently and hence |
|
123 // may be inconsistent with each other. So, this provides a |
|
124 // defensive way of performing the subtraction and avoids the value |
|
125 // going negative (which would mean a very large result, given that |
|
126 // the parameter are size_t). |
|
127 static size_t subtract_up_to_zero(size_t x, size_t y) { |
|
128 if (x > y) { |
|
129 return x - y; |
|
130 } else { |
|
131 return 0; |
|
132 } |
|
133 } |
|
134 |
|
135 protected: |
60 protected: |
136 G1CollectedHeap* _g1h; |
61 G1CollectedHeap* _g1h; |
137 |
62 |
138 // Would only be called from subclasses. |
63 // Would only be called from subclasses. |
139 G1MemoryPoolSuper(G1CollectedHeap* g1h, |
64 G1MemoryPoolSuper(G1CollectedHeap* g1h, |