복붙노트

[SPRING] Regex wild wild : java.util.regex.Pattern matcher가 높은 CPU 루프로 진입합니다.

SPRING

Regex wild wild : java.util.regex.Pattern matcher가 높은 CPU 루프로 진입합니다.

참고 : 나는이 질문을 보았지만 아무도 아직 대답하지 않았으므로별로 도움이되지 않습니다. 그리고 이상하게도 "중복 가능성"으로 표시된 질문은 삭제되었습니다 (처음 본 적이 있습니다).

패턴을 사용하여 정규 표현식 검사에 문제가 있습니다. 이 모든 일은 우리 코드에서 일어나지 않으며, Spring Framework와 Hibernate의 검증 과정에서 모든 일이 일어나고있다.

(Spring 3.2.1, Spring 3.1.1, Hibernate Validation 4.2.0)

이 호출은 @Valid 주석으로 Spring Framework @ModelAttribute 주석을 검증하려고 시도하고 있습니다.

@RequestMapping("/foo/bar")
public String doFooBar(@Valid @ModelAttribute("fooBarForm") FooBar form) 

유효성이 검사 된 FooBar 객체의 필드에는 다음과 같은 @Pattern 주석이 있습니다.

public class FooBar implements Serializable{
    @Length(min=0,max=22) @Pattern(regexp=ValidPattern.MYVALIDPATTERN)
    private String myField;

FooBar 클래스에는 자체적 인 계단식 유효성 검사가있는 다른 사용자 지정 개체도 포함되어 있습니다.

ValidPattern.MYVALIDPATTERN의 유효성 검사 패턴은 다음과 같습니다.

^([\w\-,:'"\.\?+_#~!@#$&*() /]*|(?:<sup>&trade;</sup>)*|(?:<sup>&reg;</sup>)*|(?:<sup>&copy;</sup>)*)*$

이 유효성 검사가 호출되면 99.99 %의 시간 동안 정상적으로 작동합니다. 그러나 적어도 하루에 한 번은 스레드가 어떻게 든 전체 서버에서 "도망칩니다". 그러면 수동으로 죽여야합니다. 그렇지 않으면 결국 스택 오버플로가 발생합니다.

스레드를 죽이면 스레드가이 Pattern 클래스에서 멈추었 고 계속 반복되는 것을 발견합니다 (아래 스택 추적). 이 문제를 해결하는 방법에 대한 아이디어가 있습니까?

[Top of stack]

java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$6.isSatisfiedBy(Pattern.java:4780)
java.util.regex.Pattern$CharProperty.match(Pattern.java:3362)
java.util.regex.Pattern$Curly.match0(Pattern.java:3777)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.match(Pattern.java:4312)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$Curly.match0(Pattern.java:3799)
java.util.regex.Pattern$Curly.match(Pattern.java:3761)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$Loop.matchInit(Pattern.java:4331)
java.util.regex.Pattern$Prolog.match(Pattern.java:4268)
java.util.regex.Pattern$Begin.match(Pattern.java:3137)
java.util.regex.Matcher.match(Matcher.java:1138)
java.util.regex.Matcher.matches(Matcher.java:519)
org.hibernate.validator.constraints.impl.PatternValidator.isValid(PatternValidator.java:52)
org.hibernate.validator.constraints.impl.PatternValidator.isValid(PatternValidator.java:28)
org.hibernate.validator.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:278)
org.hibernate.validator.engine.ConstraintTree.validateConstraints(ConstraintTree.java:153)
org.hibernate.validator.engine.ConstraintTree.validateConstraints(ConstraintTree.java:117)
org.hibernate.validator.metadata.MetaConstraint.validateConstraint(MetaConstraint.java:84)
org.hibernate.validator.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:452)
org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:397)
org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:361)
org.hibernate.validator.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:313)
org.hibernate.validator.engine.ValidatorImpl.validateCascadedConstraint(ValidatorImpl.java:613)
org.hibernate.validator.engine.ValidatorImpl.validateCascadedConstraints(ValidatorImpl.java:478)
org.hibernate.validator.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:322)
org.hibernate.validator.engine.ValidatorImpl.validateCascadedConstraint(ValidatorImpl.java:613)
org.hibernate.validator.engine.ValidatorImpl.validateCascadedConstraints(ValidatorImpl.java:478)
org.hibernate.validator.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:322)
org.hibernate.validator.engine.ValidatorImpl.validate(ValidatorImpl.java:139)
org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:102)
org.springframework.validation.DataBinder.validate(DataBinder.java:772)
org.springframework.web.method.annotation.ModelAttributeMethodProcessor.validateIfApplicable(ModelAttributeMethodProcessor.java:159)
org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:107)

[Abbreviated for brevity]

해결법

  1. ==============================

    1.pobrelkey와 David Wallace의 대답은 모두 정확하지만 여기에 대한 설명이 더 있습니다 ...

    pobrelkey와 David Wallace의 대답은 모두 정확하지만 여기에 대한 설명이 더 있습니다 ...

    이 정규 표현식이 "거칠게"움직이는 이유 (위대한 제목 BTW)는 대격변의 후퇴를 겪고 있기 때문입니다. 고전어 : / ^ (A *) * $ / 양식이 있습니다. 이 런 어웨이 동작은 패턴이 대상 문자열과 일치하지 않는 경우에만 발생합니다.

    가출 패턴 : ^ (A * | B * | C * | D *) * $을 수정하기위한 몇 가지 옵션이 있습니다.

    두 번째 두 개는 첫 번째 비트보다 훨씬 빠르게 수행되어야하지만 세 개 모두 "정규식이 사라진"문제를 해결할 것입니다. (물론 HTML을 정규식으로 파싱하지 않는 것이 가장 좋습니다.)

  2. ==============================

    2.길이가 0 인 서브 머티리얼을 무한히 여러 번 포함 할 수있는 (즉, (* *) 형태의 무언가 *) 정규 표현식을 작성하지 마십시오.

    길이가 0 인 서브 머티리얼을 무한히 여러 번 포함 할 수있는 (즉, (* *) 형태의 무언가 *) 정규 표현식을 작성하지 마십시오.

    패턴 고정 버전 :

    ^(?:[\w\-,:'"\.\?+_#~!@#$&*() /]|<sup>&trade;</sup>|<sup>&reg;</sup>|<sup>&copy;</sup>)*$
    
  3. ==============================

    3.각 하위 패턴 다음에 별을 제거해보십시오. 그들은 패턴이 일치하는 것에 어떤 변화도주지 않지만 JVM이 모든 종류의 경로를 찾아 일치를 찾습니다.

    각 하위 패턴 다음에 별을 제거해보십시오. 그들은 패턴이 일치하는 것에 어떤 변화도주지 않지만 JVM이 모든 종류의 경로를 찾아 일치를 찾습니다.

  4. from https://stackoverflow.com/questions/19990609/regex-gone-wild-java-util-regex-pattern-matcher-goes-into-high-cpu-loop by cc-by-sa and MIT license