如何改进我的Java APP以获得更好的性能?

时间:2017-11-05 07:33:00

标签: java spring performance memory jvm

我对这类电台的演出有一些疑问(现在我的项目使用Spring Boot)。正如您将很快理解的那样,我在一次学习大量信息时有点困惑。所以我将分享我的不同类型的问题和一些测试,以了解流程并帮助像我这样困惑的其他人:

1)阅读像这样的application.properties:

@Value("${foo.name}")
private String name;

vs读取以poo开头的属性并使用getter获取其值。

@ConfigurationProperties(prefix = "foo")
public class FooBean {
   private String name;
   private String age;
   ...

   // setters and getters
}

1a)那么哪种方法更快,为什么?如果存在差异,你能用记忆表现来解释吗? 1b)也可以说我使用FooBean方法,在Example1类中我只需要FooBean的名字。我应该将FooBean注入Example1类,而我只需要1个字段,或者我应该使用@Value样式并仅获取特定字段,现在性能更好吗?

2)我应该将FooBean的字段声明为静态吗?让我们看一个例子:

配置类:

@Component
@ConfigurationProperties(prefix = "openstack")
@Data
@Validated
public class OpenStackBean {

    @NotNull
    @Valid
    private String container;

    @NotNull
    @Valid
    private String keystoneEndpoint;

    @NotNull
    @Valid
    private String password;

    @NotNull
    @Valid
    private String swiftEndpoint;

    @NotNull
    @Valid
    private String tenantName;

    @NotNull
    @Valid
    private String userName;
}

并且假设这个类仅由其服务使用:

@Service
public class OpenStackService implements IOpenStackService {

    private OpenStackBean openStackBean;

    @Autowired
    public OpenStackService(OpenStackBean openStackBean) {
        this.openStackBean = openStackBean;
    }

...
...


private OSClientV2 authenticate(Facing perspective) throws AuthenticationException {
        return OSFactory
                .builderV2()
                .endpoint(openStackBean.getKeystoneEndpoint())
                .credentials(openStackBean.getUserName(), blowfish.decryptString(openStackBean.getPassword()))
                .tenantName(openStackBean.getTenantName())
                .perspective(perspective)
                .withConfig(Config
                        .newConfig()
                        .withConnectionTimeout(applicationBean.getConnectionTimeout())
                        .withReadTimeout(applicationBean.getReadTimeout())
                        .withMaxConnections(10)
                        .withMaxConnectionsPerRoute(2))
                .authenticate();
    }

如果我是正确的,情况将是这样的:

1)在这种情况下,我将把这些配置作为实例注入。 2)所以每个类(假设我在一分钟内有10.000个用户,这个类将被创建10.000次)将在堆栈中创建自己的配置,它将在完成时删除它,因此内存再次空闲。 3)但是因为会有这么多的请求,它不会造成内存问题吗?

如果此流程正确,那么我应该将OpenStackBean的字段创建为静态并将其注入private static OpenStackBean?这将在堆中保留一次,所有用户都将从那里获取它。哪种表现更好?

3)这是我的一些测试结果,这让我开始考虑字符串操作'和注射的表现。以下是使用JMeter进行的样本压力测试,并使用Java VisualVM进行可视化

Before the test

发送10.000个请求

after the test - report 1 after the test - report 2 after the test - report 3

但我不明白为什么堆使用总是会增加。以下是APP的工作原理:

@RequestMapping(method = RequestMethod.GET)
    public ResponseEntity all(@ApiIgnore GameParam param) {
        try {
            // get games by categories by default
            if (param.getPageNo() == null) {
                return new ResponseEntity<>(gameService.getGamesByCategories(), HttpStatus.OK);
            }

所有请求都进入此条件并调用:

2)调用此函数。由于classifiedizedGameList是静态的(在APP启动时初始化,并且每天由调度程序从远程服务器更新),所有请求都直接返回2步。 GameList位于堆中,因此不会创建新实例。

GameList - &gt; 14 KB(14173字节)

@Override
    public List<CategoryVO> getGamesByCategories() {
        if (categorizedGameList != null) {
            return categorizedGameList;
        } else {
            return getAndCategorizeGames();
        }
    }

3)为什么10000次请求后堆增加300MB(从100到400)?这意味着在内存中为每个请求创建30 KB。

3a)请求到达端点时到底发生了什么?是否将创建APP的所有实例变量或仅创建访问过的类?我这样想:

  • 用户创建与我的终端的连接
  • 用户将被路由到我的Controller类,因此该类在内存中创建。 (不是应用程序的所有类)。它的实例变量仅为此用户创建,静态字段将从内存中为所有用户提供。
  • 调用Controller的服务。因此,此服务也是为该用户创建的。
  • 将静态列表返回给该用户。这不会增加我的内存14KB,因为它已经在堆中了。
  • 但我的记忆每个请求增加30 KB。

我知道这对于阅读来说太长了,但我试图在这条道路上给出我所有的经验。我对提高自己的表现感到有些困惑,因为我意识到我从未想过它。谢谢你的耐心等待。

1 个答案:

答案 0 :(得分:0)

  
      
  1. 在这种情况下,我将注入那些配置作为实例。 2)因此,每个课程(假设我在一分钟内有10.000个用户,   类将创建10.000次)将创建自己的   堆栈中的配置,完成后将其删除,   因此,内存再次可用。 3)但是因为会有很多请求,   会不会引起内存问题?
  2.   

不,这不是问题。缺省情况下,Spring Bean是“ Singleton”作用域(除非您显式设置了另一个作用域),因此将仅创建 OpenStackBean 的一个实例。 因此,无需创建静态字段。

  

那么为什么在10000个请求后,堆增加300MB(从100到400)?这意味着将为每个请求在内存中创建30 KB。

因为tomcat需要创建许多临时对象来处理请求,但是在请求得到响应后它们将消失(还请注意,由于Java仅在垃圾回收之后才删除未使用的对象,因此堆使用率不会降低)< / p>

  

调用控制器的服务。因此,为此创建了此服务   用户。

这也是错误的,如果您的控制器是Singletone作用域,则仅创建一次。

  

当请求到达端点时会发生什么?是要创建APP的所有实例变量,还是仅创建访问的类?

不骂人!当tomcat服务您的请求时,仅需要实例化cals。

通常,您已经结束了有关性能问题的关注。您应该关注的事情是何时未从内存中删除对象(GC使用直到应用程序崩溃后,内存使用量增加且没有减少)。同样涉及内存占用是真实的,但与通过次要GC创建和销毁的一些小对象无关。