Skip to content

Performance optimization in AbstractBeanFactoryBasedTargetSource.hashCode() #30576

Closed
@jengebr

Description

@jengebr

Affects: 5.2.20

Our production application shows 2.8% of spent cpu coming from the following stack trace:

java.lang.Object.hashCode
org.springframework.util.ObjectUtils.nullSafeHashCode
org.springframework.aop.target.AbstractBeanFactoryBasedTargetSource.hashCode
org.springframework.aop.framework.CglibAopProxy$HashCodeInterceptor.intercept
<proprietary>$SpringCGLIB-Proxy.hashCode
<proprietary>.hashCode

Reviewing the code for AbstractbeanFactoryBasedTargetSource.hashCode(), we see:

@Override
public int hashCode() {
	int hashCode = getClass().hashCode();
	hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.beanFactory);
	hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.targetBeanName);
	return hashCode;
}

this.targetBeanName is a String, and therefore never routes toObject.hashCode(). That leaves this.beanFactory as the only possible code path, and indeed there is no hashCode() defined on the interface or any bundled implementation.

Modifying our custom bean factory (extends DefaultListableBeanFactoryWithPublicMutex) with the following methods solves the problem:


    /**
     * This should be a singleton, hashcode doesn't matter unless somebody else is
     * silly enough to use it. See AbstractBeanFactoryBasedTargetSource.hashCode
     * (which uses it).
     * 
     * This implementation is effectively free, and certainly much faster than
     * Object.hashCode().
     */
    @Override
    public int hashCode() {
        return 0;
    }

    /**
     * This should be a singleton, use identity equals.
     */
    @Override
    public boolean equals(Object o) {
        return (this == o);
    }

I have two suggestions for how to solve this within the framework:

  1. Modify AbstractBeanFactoryBasedTargetSource.hashCode() to not consider the bean factory. This is okay because 1) bean factory cardinality is extremely low and most beans have the same factory, and 2) the identity check in equals is nearly-free and, frankly, faster than calculating the hashcode.
  2. Accelerate the hashCode() method by implementing the method on the most common superclasses. One suggestion is to calculate a random number on object instantiation and keep that around as the hashcode. This serves the purpose of almost-uniquely identifying the BeanFactory instance, and the memory penalty of an int is small compared to the overall usage of most BeanFactory implementations.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchestype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions