Details

    • Type: Bug Bug
    • Status: Resolved Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: JRuby 1.5, JRuby 1.5.1, JRuby 1.5.2
    • Fix Version/s: JRuby 1.7.0.pre2
    • Component/s: None
    • Labels:
      None
    • Environment:
      JRuby 1.5.x
      Oracle JRE 1.6.0_21 32 and 64 bit where applicable
      Windows XP, Windows 2003 Enterprise 32-bit, Windows 2003 Enterprise 64-bit, Windows 7 32-bit, Windows 7 64 bit
    • Testcase included:
      yes
    • Patch Submitted:
      Yes
    • Number of attachments :
      0

      Description

      test script and patch here:
      http://gist.github.com/558316

      Note: this bug does not occur on Windows 2008 32-bit, Windows 2008 64-bit, or Windows 2008 R2

        Activity

        Hide
        John Firebaugh added a comment -

        Here's a simplified test case: https://gist.github.com/974975

        If you remove the assignment to the constant SHGetFolderPath, it doesn't leak.

        Show
        John Firebaugh added a comment - Here's a simplified test case: https://gist.github.com/974975 If you remove the assignment to the constant SHGetFolderPath, it doesn't leak.
        Hide
        John Firebaugh added a comment -

        Here's a test case that reproduces on OS X: https://gist.github.com/975179

        You have to wait for 32 or more iterations to see evidence of the leak, but it's there. The key to reproducing it seem to be the use of FFI::Function#attach to a constant. This leads me to suspect that the problem revolves around the WeakHashMap "refmap" in org.jruby.ext.ffi.AbstractInvoker. Here's my hypothesis:

        The WeakHashMap holds a strong reference to the AbstractInvoker as long as the associated DynamicMethod key is reachable. The DynamicMethod is reachable so long as the object it's attached to is reachable. When attached to a Ruby constant, that constant is reachable as long as its runtime is reachable. And the runtime is always reachable because there's a reference to it in the AbstractInvoker held in the WeakHashMap.

        This is similar to the memory leak I just tracked down in the JRuby JSON library: https://github.com/flori/json/pull/74. I'm having doubts about the patch I submitted there though, so I'd appreciate having someone take a second look at both this and that.

        Show
        John Firebaugh added a comment - Here's a test case that reproduces on OS X: https://gist.github.com/975179 You have to wait for 32 or more iterations to see evidence of the leak, but it's there. The key to reproducing it seem to be the use of FFI::Function#attach to a constant. This leads me to suspect that the problem revolves around the WeakHashMap "refmap" in org.jruby.ext.ffi.AbstractInvoker. Here's my hypothesis: The WeakHashMap holds a strong reference to the AbstractInvoker as long as the associated DynamicMethod key is reachable. The DynamicMethod is reachable so long as the object it's attached to is reachable. When attached to a Ruby constant, that constant is reachable as long as its runtime is reachable. And the runtime is always reachable because there's a reference to it in the AbstractInvoker held in the WeakHashMap. This is similar to the memory leak I just tracked down in the JRuby JSON library: https://github.com/flori/json/pull/74 . I'm having doubts about the patch I submitted there though, so I'd appreciate having someone take a second look at both this and that.
        Hide
        Ben Browning added a comment - - edited

        This issue appears more generic than just constants - I'm seeing DynamicMethod instances themselves being held onto in org.jruby.RubyModule's methods map. So, just like with constants, this means that the Ruby is always keeping a strong reference to the DynamicMethod around and thus the DynamicMethod key in the refmap WeakHashMap will never get garbage collected as long as the Ruby is around. The Ruby will stay around as long as the refmap entry exists.

        I opened https://github.com/jruby/jruby/issues/203 before seeing the issue here but they will likely be fixed by the same thing, even though this JIRA is about a more specific manifestation of this leak.

        Show
        Ben Browning added a comment - - edited This issue appears more generic than just constants - I'm seeing DynamicMethod instances themselves being held onto in org.jruby.RubyModule's methods map. So, just like with constants, this means that the Ruby is always keeping a strong reference to the DynamicMethod around and thus the DynamicMethod key in the refmap WeakHashMap will never get garbage collected as long as the Ruby is around. The Ruby will stay around as long as the refmap entry exists. I opened https://github.com/jruby/jruby/issues/203 before seeing the issue here but they will likely be fixed by the same thing, even though this JIRA is about a more specific manifestation of this leak.
        Hide
        Ben Browning added a comment -

        I've sent a pull request (https://github.com/jruby/jruby/pull/205) to fix this issue. Here's the output of the OS X test case provided by John before and after the fix:

        before:
        jruby 1.7.0.dev (ruby-1.9.3-p139) (2012-05-04 b0c4eaa) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_31) [darwin-x86_64-java]
        loop: 1 time: 0.7300 mem: 85000192
        loop: 2 time: 0.8800 mem: 85000192
        loop: 3 time: 0.2900 mem: 85000192
        ...
        loop: 50 time: 0.1200 mem: 226222080
        loop: 51 time: 0.3400 mem: 230547456
        loop: 52 time: 0.1000 mem: 234803200

        after:
        jruby 1.7.0.preview1 (ruby-1.9.3-p203) (2012-06-12 1a7fe2f) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_31) [darwin-x86_64-java]
        loop: 1 time: 1.3800 mem: 85000192
        loop: 2 time: 1.2400 mem: 85000192
        loop: 3 time: 0.4000 mem: 85000192
        ...
        loop: 50 time: 0.1500 mem: 85000192
        loop: 51 time: 0.1300 mem: 85000192
        loop: 52 time: 0.1000 mem: 85000192
        ...
        loop: 125 time: 0.0800 mem: 85000192

        Show
        Ben Browning added a comment - I've sent a pull request ( https://github.com/jruby/jruby/pull/205 ) to fix this issue. Here's the output of the OS X test case provided by John before and after the fix: before: jruby 1.7.0.dev (ruby-1.9.3-p139) (2012-05-04 b0c4eaa) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_31) [darwin-x86_64-java] loop: 1 time: 0.7300 mem: 85000192 loop: 2 time: 0.8800 mem: 85000192 loop: 3 time: 0.2900 mem: 85000192 ... loop: 50 time: 0.1200 mem: 226222080 loop: 51 time: 0.3400 mem: 230547456 loop: 52 time: 0.1000 mem: 234803200 after: jruby 1.7.0.preview1 (ruby-1.9.3-p203) (2012-06-12 1a7fe2f) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_31) [darwin-x86_64-java] loop: 1 time: 1.3800 mem: 85000192 loop: 2 time: 1.2400 mem: 85000192 loop: 3 time: 0.4000 mem: 85000192 ... loop: 50 time: 0.1500 mem: 85000192 loop: 51 time: 0.1300 mem: 85000192 loop: 52 time: 0.1000 mem: 85000192 ... loop: 125 time: 0.0800 mem: 85000192
        Hide
        Thomas E Enebo added a comment -

        fixed in commit cfbb64d (patch by Ben Browning)

        Show
        Thomas E Enebo added a comment - fixed in commit cfbb64d (patch by Ben Browning)

          People

          • Assignee:
            Thomas E Enebo
            Reporter:
            Mike Luu
          • Votes:
            1 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: