It took me some time to figure out how to run the tests. For one thing, it 
didn't like the fact that I wasn't using the default repository (I wasn't 
using .m2/repository in my home directory). Eventually, I got it working by 
using that default repository.

Anyway, I'm still working on writing a test.

In the meantime, several observations (I noted #2 when originally writing 
my patch, but did not change it):

1. Log4J1 in pax-logging-api behaves differently from SLF4J in 
pax-logging-api in your Log4JMemoryTest.

    If you take the Log4JMemoryTest and *use the Log4J1 API instead of 
SLF4J API* in the MyClass to get the nonStaticLogger, then *you end up with 
1,000,000 Loggers within m_loggers in org.apache.log4j.Logger in 
pax-logging-api*. To make this change, I changed only the MyClass 
constructor, to be:
        public MyClass(String name) {
            this.nonStaticLogger = Logger.getLogger(name);
        }

and changed the imports from
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

to

import org.apache.log4j.Logger;

*This behavior is what my patch addresses. With my patch, there were at 
maximum 1,535 entries in m_loggers. (The 1,535 varies as garbage collection 
is not deterministic, but 1,535 is still better than 1,000,000.)*

===================
===================
2. Explanation of #1 (difference in whether or not the new logger instance 
is stored in m_loggers when m_paxLogging is not null):

   - SLF4J in pax-logging-api: 
   In org.ops4j.pax.logging.slf4j.Slf4jLoggerFactory getLogger, *if 
   m_paxLogging is not null* (Line 95), then a new Slf4jLogger is created, *but 
   the Slf4jLogger is not stored in m_loggers.*
   - Log4J1 in pax-logging-api*: *In org.apache.log4j.Logger getLogger, the 
   new Logger created on line 140 is *stored in m_loggers regardless of 
   whether m_paxLogging is null or not null.*
   - Others in pax-logging-api: The other log implementations (Apache 
   Commons Logging in org.apache.commons.logging.LogFactory, JULI Logging in 
   org.apache.juli.logging.LogFactory, Avalon Logging in 
   org.ops4j.pax.logging.avalon.AvalonLogFactory) *behave like 
   org.apache.log4j.Logger*, and *store new logger instance in m_loggers 
   regardless of whether m_paxLogging is null or not null.*
   - 
*Only org.ops4j.pax.logging.log4jv2.Log4jv2LoggerContext checks for 
   existence of a logger with the same name before creating a new logger to 
   put in the loggers map. *


===================
===================
I'll look at writing a test to match my usage with what I called "groups". 
Markers as the Ralph from Log4J2 suggested may help (I haven't tried how 
that works yet---I've just been using the existing logging we've had for 
years). However, without changing our own logging, the change in behavior 
of pax-logging-api between the very old 1.6.1 and the new 1.10.5 because of 
the change in the order of WeakHashMap parameters definitely had a negative 
effect on the memory behavior of pax-logging-api. Even if the 
TrackingLogger using the same name sticks around after we're done with that 
(I can deal with that), the fact that the pax-logging-api holds on to 
things longer doesn't seem too good. I guess since I now use 
pax-logging-api Log4J2 instead of pax-logging-api Log4J1, that does help, 
because Log4J2 in pax-logging-api only creates new instances for new names. 
Log4J1, as stated in #1 above, creates and holds onto new instances 
forever, even if the name is re-used.

Thanks again,
Monica

On Saturday, March 14, 2020 at 6:13:14 AM UTC-4, Grzegorz Grzybek wrote:
>
> Hello again
>
> I've backported 3 "memory" tests from Pax Logging 1.11.x to 
> https://github.com/ops4j/org.ops4j.pax.logging/commits/pax-logging-1.10.x
>
> The test (e.g., org.ops4j.pax.logging.it.Log4J2MemoryTest) does this:
>
> @Test
> public void memoryIssues() throws IOException {
>     LOG.info("Starting");
>     String[] loggerNames = new String[10000];
>     for (int i = 0; i < 10000; i++) {
>         loggerNames[i] = UUID.randomUUID().toString();
>     }
>     for (int i = 0; i < 1000000; i++) {
>         if (i % 10000 == 0) {
>             LOG.info("iteration {}", i);
>             System.gc();
>         }
>         new Log4J2MemoryTest.MyClass(loggerNames[i % 10000]).run();
>     }
>     LOG.info("Done");
> }
>
> private static class MyClass {
>     private Logger nonStaticLogger;
>
>     public MyClass(String name) {
>         this.nonStaticLogger = LoggerFactory.getLogger(name);
>     }
>
>     public void run() {
>         // running a method
>         nonStaticLogger.trace("Hello!");
>     }
> }
>
> You see - million logger instances using 10000 different unique names. 
> What I'm not sure about is the "group" you've mentioned - are the new 
> groups reusing logger names? or each "group" introduces completely new set 
> of loggers?
>
> I checked the result in heap dump and I got:
>
> [image: image.png]
>
> which means that in pax-logging 1.10.6-SNAPSHOT (not changed comparing to 
> 1.10.5) there's exactly 10000 TrackingLoggers (+11 for non-test related 
> ones).
>
> The test uses SLF4J facade to get pax logging loggers.
>
> May I ask you to change the tests, so I can try reproducing your problem? 
> I agree that using WeakHashMap helps, but if the number of unique logger 
> names doesn't increase endlessly, I think it's not necessary to use 
> WeakHashMaps.
>
> BTW, I've closed https://issues.apache.org/jira/browse/LOG4J2-2806 even 
> if I was able to ensure proper weak-reference behavior inside Log4J2.
>
> Thanks for great cooperation!
>
> regards
> Grzegorz Grzybek
>

-- 
-- 
------------------
OPS4J - http://www.ops4j.org - [email protected]

--- 
You received this message because you are subscribed to the Google Groups 
"OPS4J" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/ops4j/e2090a5d-ad39-49d6-8a6d-c9dd8869a1b1%40googlegroups.com.

Reply via email to