<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Jun 25, 2015 at 1:28 PM, Tony Printezis <span dir="ltr"><<a href="mailto:tprintezis@twitter.com" target="_blank">tprintezis@twitter.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div style="font-family:Helvetica,Arial;font-size:13px;color:rgb(0,0,0);margin:0px">Hi Jeremy,</div><div style="font-family:Helvetica,Arial;font-size:13px;color:rgb(0,0,0);margin:0px"><br></div><div style="font-family:Helvetica,Arial;font-size:13px;color:rgb(0,0,0);margin:0px">Inline.</div><span class=""> <br><p style="color:rgb(0,0,0)">On June 24, 2015 at 7:26:55 PM, Jeremy Manson (<a href="mailto:jeremymanson@google.com" target="_blank">jeremymanson@google.com</a>) wrote:</p> </span><div><span class=""><div><blockquote type="cite" style="color:rgb(0,0,0);font-family:Helvetica,Arial;font-size:13px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span><div><div><div dir="ltr"><div class="gmail_extra"><br><br><div class="gmail_quote">On Wed, Jun 24, 2015 at 10:57 AM, Tony Printezis<span> </span><span dir="ltr"><<a href="mailto:tprintezis@twitter.com" target="_blank">tprintezis@twitter.com</a>></span><span> </span>wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div style="font-family:Helvetica,Arial;font-size:13px;color:rgb(0,0,0);margin:0px">Hi Jeremy,</div><div style="font-family:Helvetica,Arial;font-size:13px;color:rgb(0,0,0);margin:0px"><br></div><div style="font-family:Helvetica,Arial;font-size:13px;color:rgb(0,0,0);margin:0px">Please see inline.</div><span><br></span><p style="color:rgb(0,0,0)"><span>On June 23, 2015 at 7:22:13 PM, Jeremy Manson (<a href="mailto:jeremymanson@google.com" target="_blank">jeremymanson@google.com</a>) wrote:</span></p><div><blockquote type="cite" style="color:rgb(0,0,0);font-family:Helvetica,Arial;font-size:13px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><div><div dir="ltr"><span>I don't want the size of the TLAB, which is ergonomically adjusted, to be tied to the sampling rate.  There is no reason to do that.  I want reasonable statistical sampling of the allocations.  </span></div></div></div></blockquote></div><p><br></p><p>As I said explicitly in my e-mail, I totally agree with this. Which is why I never suggested to resize TLABs in order to vary the sampling rate. (Apologies if my e-mail was not clear.)</p></div></blockquote><div><br></div><div>My fault - I misread it.  Doesn't your proposal miss out of TLAB allocs entirely</div></div></div></div></div></div></span></blockquote></div><p><br></p></span><p>This is correct: We’ll also have to intercept the outside-TLAB allocs. But, IMHO, this is a feature as it’s helpful to know how many (and which) allocations happen outside TLABs. These are generally very infrequent (and slow anyway), so sampling all of those, instead of only sampling some of them, does not have much of an overhead. But, you could also do sampling for the outside-TLAB allocs too, if you want: just accumulate their size on a separate per-thread counter and sample the one that bumps that counter goes over a limit.</p><p></p></div></div></blockquote><div><br></div><div>The outside-TLAB allocations generally get caught anyway, because they tend to be large enough to jump over the sample size immediately.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><p>An additional observation (orthogonal to the main point, but I thought I’d mention it anyway): For the outside-TLAB allocs it’d be helpful to also know which generation the object ended up in (e.g., young gen or direct-to-old-gen). This is very helpful in some situations when you’re trying to work out which allocation(s) grew the old gen occupancy between two young GCs.</p></div></div></blockquote><div><br></div><div>True.  We don't have this implemented, but it would be reasonably straightforward to glean it from the oop.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><p>FWIW, the existing JFR events follow the approach I described above:</p><p>* one event for each new TLAB + first alloc in that TLAB (my proposal basically generalizes this and removes the 1-1 relationship between object alloc sampling and new TLAB operation)</p><p>* one event for all allocs outside a TLAB</p><p>I think the above separation is helpful. But if you think it could confuse users, you can of course easily just combine the information (but I strongly believe it’s better to report the information separately).</p></div></blockquote><div><br></div><div>I do think it would make a confusing API.  It might make more sense to have a reporting mechanism that had a set number of fields with very concrete information (size, class, stacktrace), but allowed for platform-specific metadata.  We end up with a very long list of things we want in the sample: generation (how do you describe a generation?), object age (by number of GCs survived?  What kind of GC?), was it a TLAB allocation, etc.</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><span class=""><div><blockquote type="cite" style="color:rgb(0,0,0);font-family:Helvetica,Arial;font-size:13px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span><div><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>(and, in fact, not work if TLAB support is turned off)? </div></div></div></div></div></div></span></blockquote></div><p><br></p></span><p>Who turns off TLABs? Is -UseTLAB even tested by Oracle? (This is a genuine question.)</p></div></div></blockquote><div><br></div><div>I don't think they do.  I have turned them off for various reasons (usually, I'm trying to instrument allocations and I don't want to muck about with thinking about TLABs), and the code paths seem a little crufty.  ISTR at some point finding something that clearly only worked by mistake, but I can't remember now what it was.</div><div> <br></div><div>[snip]</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><div><div><div><div><div><div><span class=""><div><blockquote type="cite" style="color:rgb(0,0,0);font-family:Helvetica,Arial;font-size:13px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><div><div><div><blockquote type="cite" style="color:rgb(0,0,0);font-family:Helvetica,Arial;font-size:13px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><div><div dir="ltr"><div><div><div><div><span>  However, you can do pretty much anything from the VM itself.  Crucially (for us), we don't just log the stack traces, we also keep track of which are live and which aren't.  We can't do this in a callback, if the callback can't create weak refs to the object.</span></div><span><br>What we do at Google is to have two methods: one that you pass a callback to (the callback gets invoked with a StackTraceData object, as I've defined above), and another that just tells you which sampled objects are still live.  We could also add a third, which allowed a callback to set the sampling interval (basically, the VM would call it to get the integer number of bytes to be allocated before the next sample).  </span></div><div><span><br></span></div><div><span>Would people be amenable to that?  It makes the code more complex, but, as I say, it's nice for detecting memory leaks ("Hey!  Where did that 1 GB object come from?").</span></div></div></div></div></div></div></blockquote></div><p><br></p><p>Well, that 1GB object would have most likely been allocated outside a TLAB and you could have identified it by instrumenting the “outside-of-TLAB allocation path” (just saying…).</p></div></div></div></div></div></div></blockquote><div><br></div><div>That's orthogonal to the point I was making in the quote above - the point I was making there was that we want to be able to detect what sampled objects are live.  We can do that regardless of how we implement the sampling (although it did involve my making a new kind of weak oop processing mechanism inside the VM).</div></div></div></div></span></blockquote></div><p><br></p></span><p>Yeah, I was thinking of doing something similar (tracking object lifetimes, and other attributes, with WeakRefs). </p></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>We have all of that implemented, so hopefully I can save you the trouble. :) </div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><div><div><div><div><div><div><div><span class=""><div><blockquote type="cite" style="color:rgb(0,0,0);font-family:Helvetica,Arial;font-size:13px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>But to the question of whether we can just instrument the outside-of-tlab allocation path...  There are a few weirdnesses here.  The first one that jumps to mind is that there's also a fast path for allocating in the YG outside of TLABs, if an object is too large to fit in the current TLAB.  Those objects would never get sampled.  So "outside of tlab" doesn't always mean "slow path".</div></div></div></div></span></blockquote></div><p><br></p></span><p>CollectedHeap::common_mem_allocate_noinit() is the first-level of the slow path called when a TLAB allocation fails because the object doesn’t fit in the current TLAB. It checks (alocate_from_tlab() <font face="Helvetica">/ allocate_from_tlab_slow()) whether to refill the current TLAB or keep the TLAB and delegate to the GC (mem_allocate()) to allocate the object outside a TLAB (either in the young or old gen; the GC might also decide to do a collection at this point if, say, the eden is full...). So, it depends on what you mean by slow path but, yes, any alloocations that go through the above path should be considered as “slow path” allocations.</font></p></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>Let me be more specific.  Here is a place where allocations go through a fast path that is outside of a TLAB:</div><div><br></div><div><a href="http://hg.openjdk.java.net/jdk9/dev/hotspot/file/972580a0eef8/src/cpu/x86/vm/templateTable_x86.cpp#l3759">http://hg.openjdk.java.net/jdk9/dev/hotspot/file/972580a0eef8/src/cpu/x86/vm/templateTable_x86.cpp#l3759</a><br></div><div><br></div><div>If the object won't fit in the TLAB, but will fit in the Eden, it will be allocated in the Eden, with hand-generated assembly.  This case will be entirely missed by sampling just the TLAB creation (or your variant) and the slow path.  I may be missing something about that code, but I can't really see what it is.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><div><div><div><div><div><div><div><p><font face="Helvetica">One more piece of data: AllocTracer::send_allocation_outside_tlab_event() (the JFR entry point for outside-TLAB allocs) is fired from common_mem_allocate_noint(). So, if there are other non-TLAB allocation paths outside that method, that entry point has been placed incorrectly (it’s possible of course; but I think that it’s actually placed correctly).</font></p></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>What is happening in the line to which I referred, then?  To me, it kind of reads like "this is close enough to being TLAB allocation that I don't care that it isn't".</div><div> </div><div>And that's really what's going on here. Your strategy is to tie what I see as a platform feature to a particular implementation.  If the implementation changes, or if we really don't understand it as well as we think we do, the whole thing falls on the floor.  If we mention TLABs in the docs, and TLABs do change, then it won't mean anything anymore.</div><div><br></div><div>A particular example pops to mind: I believe Metronome doesn't have TLABs at all.  Is that correct?  Can J9 developers implement this feature?</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span class=""><div><blockquote type="cite" style="color:rgb(0,0,0);font-family:Helvetica,Arial;font-size:13px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>For reference, to keep track of sampling, the delta to C2 is about 150 LOC (much of which is newlines-because-of-formatting for methods that take a lot of parameters), the delta to C1 is about 60 LOC, the delta to each x86 template interpreter is about 20 LOC, and the delta for the assembler is about 40 LOC.      It's not completely trivial, but the code hasn't changed substantially in the 5 years since I wrote it (other than a couple of bugfixes).</div><div><br></div><div>Obviously, assembler/template interpreter would have to be dup'd across platforms - we can do that for PPC and aarch64, on which we do active development, at least.</div></div></div></div></span></blockquote></div><p><br></p></span><p>I’ll again vote for the simplicity of having a simple change in only one place (OK, two places…).</p></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>This isn't a simple change anyway, if we're keeping track of live references.  We have to hook into reference processing - when a weak oop is detected to be dead, we have to delete the metadata.  And we have to change JVMTI.</div><div><br></div><div>Jeremy</div></div></div></div>