Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CxxPreprocessor: collect missing includes conditionally #1650

Merged

Conversation

ivangalkin
Copy link
Contributor

@ivangalkin ivangalkin commented Jan 2, 2019

  • collect missing includes only if MissingIncludeFileCheck was activated
  • don't collect successful includes - nobody uses them
  • avoid memory run-time overhead and reduce memory footprint
  • CxxParser::cxxpp - don't extend the lifetime of CxxPreprocessor artificially

Alternative (more radical) solution: #1638


This change is Reviewable

* collect missing includes only if MissingIncludeFileCheck was activated
* don't collect successful includes - nobody uses them
* avoid memory run-time overhead and reduce memory footprint
* CxxParser::cxxpp - don't extend the lifetime of CxxPreprocessor artificially

Alternative (more radical) solution: SonarOpenCommunity#1638
Copy link
Collaborator

@guwirth guwirth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ivangalkin would like to merge this next. Don’t understand the WeakReference? Think garbage collection is allowed to remove object at any time. Means get() can also return null?

@ivangalkin
Copy link
Contributor Author

@guwirth the idea is not to extend the lifetime of the CxxPreprocessor longer than necessary.

The "normal" reference (CxxParser::cxxpp) is never garbage collected, because it is static. The only way to perform its GC is to assign a null-pointer to the corresponding class field.

WeakReference doesn't increase the reference-count of the CxxPreprocessor. So it's just doesn't count in terms of GC. Other references do, and we have them in CxxSquidSensor::execute().

  public void CxxSquidSensor::execute(SensorContext context) {

    ...
    // create an AstScanner;
    // CxxAstScanner.creates() invokes  CxxParser.create() internally
    // that means, that the life-time of CxxPreprocessor is limited by the scope of
    // of CxxSquidSensor::execute()

    AstScanner<Grammar> scanner = CxxAstScanner.create(this.language, cxxConf,
      visitors.toArray(new SquidAstVisitor[visitors.size()])); 

    ...

    // now the same instance of the CxxPreprocessor is used to process all given files
    // after each file becomes processed it is analyzed by the visitors
    // among others there are two visitors, which call CxxParser's static methods
    // 1. CxxFileVisitor calls CxxParser.finishedParsing()
    // 2. MissingIncludeFileCheck calls CxxParser.getMissingIncludeFiles()
    // during these calls we still have the valid instance of CxxPreprocessor which is used
    // for these invocatoins
    scanner.scanFiles(files);

    ...
    // now the method CxxSquidSensor::execute() is over
    // the analysis of module is finished, we can destroy the preprocessor
}

As you see, there is no need to make the instance of preprocessor to the forewer-living singleton.
It's not that important for sonar-cxx itself, but after execution of our plugin is over, we should clean-up our resources. This might be crucial for the plugins, which are executed after us.

P.S. It would be more illegal to create the CxxParser on stack while execution of CxxSquidSensor::execute(). The reference could be passed to the 2 mentioned visitors while their instantiation. So there will be no need in static methods, static variables and all that complexity. However I didn't want to touch too much code.

@guwirth
Copy link
Collaborator

guwirth commented Jan 6, 2019

@ivangalkin thx for your explanation.

But I still think GC is allowed to remove it at any time. Article below says: Objects referenced by them may be garbage collected -- if the JVM runs short of memory -- even when they are being used.
http://www.programmr.com/blogs/what-every-java-developer-should-know-strong-and-weak-references

Means during scanFiles GC is allowed to remove CxxPreprocessor at any time?

@ivangalkin
Copy link
Contributor Author

@guwirth the article describes the technique correct: if some instance is referenced by no strong reference, but only by WeakReferences -> object is garbage-collected. Some of the illustrations is in this article is artificial and misleading IMHO

WeakReference<String> wr = new WeakReference<String>(new String("abc"));

Such code means indeed, that developer cannot rely on the existence of the object. It might be garbage-collected at any time. There is no strong references.

Let me illustrate the work of weak references on the following example. Please try it here: https://ideone.com/k32xaP

/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;
import java.lang.ref.*; 

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
	public static class CxxPreprocessor {};
	public static void main (String[] args) throws java.lang.Exception
	{
		CxxPreprocessor strongRef = new CxxPreprocessor();
		WeakReference<CxxPreprocessor> weakRef = new WeakReference<CxxPreprocessor>(strongRef);
		// force garbage collection; the instance of CxxPreprocessor is referenced by
		// 1 strong reference
		// 1 weak reference
		// CxxPreprocessor is not gc'ed because there is at least one strong reference 
		System.gc();
		System.out.println("After 1st GC: " + weakRef.get());
		
		// remove strong reference and force the gc again
		// there is only a weak reference to the instance of CxxPreprocessor
		// CxxPreprocessor is garbage-collected
		strongRef = null;
		System.gc();
		System.out.println("After 2nd GC: " + weakRef.get());

	}
}

In our example (see my comment above) we have highly predictable lifetime. Visitors can call into the CxxPreprocessor only while it is existing. With the weak reference we 1) avoid leaks 2) create kind of run-time check, which prevents calls into the preprocessor at the wrong time.

@guwirth guwirth merged commit 2ff6f41 into SonarOpenCommunity:master Jan 6, 2019
@guwirth guwirth mentioned this pull request Feb 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

2 participants