JavaScript 성능 최적화 (성능 및 메모리 관리)

기사 설명 : 본 기사는 레가 오 프론트 엔드 트레이닝 캠프의 경험담입니다. 뭔가 잘못된 점이 있으면 지적 해주시고 가르쳐 주시길 바랍니다. 감사합니다! 

1. 성능

성능을 사용하는 이유

  • GC의 목적은 메모리 공간의 선순환을 달성하는 것입니다.
  • 선순환의 초석은 합리적 사용이다
  • 그것이 합리적인지 항상주의를 기울이십시오
  • 성능은 여러 모니터링 방법을 제공합니다.

성능 단계

  • 브라우저를 열고 도착 URL을 입력하세요.
  • 개발자 도구 패널을 열고 성능을 선택하십시오.
  • 녹음 기능을 켜고 특정 페이지를 방문하십시오.
  • 사용자 행동을 수행하고 일정 시간 후에 기록한 다음 기록을 종료하고 성능 버튼을 클릭하여 메모리 사용량을 확인합니다.

둘째, 기억 문제의 발현

기억 문제의 외부 발현 :

  • 페이지에서 지연로드 또는 빈번한 일시 중지 (네트워크 환경은 정상 임)
  • 페이지 지속성 나쁜 성능
  • 페이지의 성능은 시간이 지남에 따라 점점 더 나빠집니다.

셋째, 메모리 모니터링의 중앙 집중식 방법

메모리 문제를 정의하기위한 표준

  • 메모리 누수 : 메모리 사용량은 계속 증가합니다.
  • 메모리 확장 : 대부분의 장치에 성능 문제가 있습니다 (최상의 결과를 얻으려면 현재 응용 프로그램 자체에 많은 메모리 공간이 필요합니다.이 과정에서 현재 장치 자체가 하드웨어를 지원하지 않을 수 있습니다. 사용 중 성능)
  • 빈번한 가비지 수집 : 메모리 변화 그래프를 통한 분석

메모리를 모니터링하는 여러 방법

  • 브라우저 작업 관리자
  • 타임 라인 시퀀스 차트 기록
  • DOM을 찾아 분리하기위한 힙 블록 사진
  • 번거로운 가비지 수집이 있는지 확인

 1. 작업 관리자는 메모리를 모니터링합니다.

바로 가기 키 shift + esc를 사용하여 브라우저 작업 관리자를 호출하고 마우스 오른쪽 버튼을 클릭하여 현재 페이지의 JavaScript 메모리를 선택하면 표시됩니다.

첫 번째 열이 차지하는 메모리는 기본 메모리입니다. 현재 인터페이스에 Dom 노드가 많이 있다는 것을 쉽게 이해할 수 있습니다.이 메모리는 Dom이 차지하는 메모리입니다. 메모리가 계속 증가하면 페이지가 계속 증가 함을 의미합니다. 새로운 돔을 만듭니다.

JavaScript에서 사용하는 마지막 메모리 열은 js의 힙을 나타냅니다.주의해야 할 것은 괄호 안의 값입니다. 도달 할 수있는 모든 객체의 크기를 나타냅니다.이 수가 증가하면 현재 Either new 객체가됩니다. 인터페이스에서 생성되거나 현재 기존 개체가 지속적으로 증가하고 있습니다.

문제가있는 경우에만 알 수 있으며 위치 문제는 사용하기 쉽지 않습니다.

2. 타임 라인 기록 메모리

타임 라인을 사용하여 메모리 변경 사항을 기록하므로 메모리 변경 사항을보다 정확하게 관찰 할 수 있습니다.

 

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>任务管理器监控内存变化</title> 
</head> 
<body>
    <button id="btn">
        Add
    </button>
<script type="text/javascript">
    const arrList = []

    function test(){
        for(let i = 0;i < 100000;i++){
            document.body.appendChild(document.createElement('p'))
        }
        arrList.push(new Array(100000).join('x'))
    }
    document.getElementById('btn').addEventListener('click',test)
</script> 
</body> 
</html>

크롬 브라우저로 html 파일을 열고 콘솔을 열고 성능 옵션을 클릭하고 녹음을 시작한 다음 페이지에서 추가 버튼을 클릭하고 간헐적으로 여러 번 클릭 한 다음 화면 녹화를 종료합니다.

 

 3. 힙 스냅 샷 검색 및 별도의 DOM

별도의 DOM이란?

  • DOM 트리에있는 인터페이스 요소
  • 가비지 객체의 DOM 노드 :이 노드가 현재 DOM 트리에서 분리되고 js 코드에서 누구도 참조하는 DOM 노드가 없으면 실제로 가비지가됩니다.
  • 분리 된 상태의 DOM 노드 :이 노드가 현재 DOM 트리에서 분리되고 js 코드에서 누군가가 참조하는 DOM 노드가있는 경우 분리 된 상태의 DOM 노드라고합니다.

예 : 버튼을 클릭하면 DOM 트리에 분리 된 DOM이 생성됩니다. 분리 된 DOM은 페이지에서 사용되지 않고 코드에서 사용되므로 공간 낭비가 발생합니다. TmpEle을 비워두고 GC가 수집하도록합니다. 찌꺼기.

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>任务管理器监控内存变化</title> 
</head> 
<body>
    <button id="btn">
        Add
    </button>
<script type="text/javascript">
    var tmpEle

    function  fn() {
        var ul = document.createElement('ul')
        for(var i = 0; i<10 ;i++ ){
            var li = document.createElement('li')
            ul.appendChild(li)
        }
        tmpEle = ul
    }

    document.getElementById('btn').addEventListener('click',fn)//js中创建的节点并没有往我们页面里添加,是分离的DOM
</script> 
</body> 
</html>

여기에 사진 설명 삽입

 위 애니메이션의 소스 : https://coder5.blog.csdn.net/article/details/109723747

4. 빈번한 GC가 있는지 확인

자주 가비지 수집을 결정하는 이유

  • GC가 작동하는 동안 응용 프로그램이 중지됩니다.
  • 빈번하고 긴 GC로 인해 응용 프로그램이 중단됩니다.
  • 사용자는 사용 중에 애플리케이션이 멈춘다 고 인식합니다.

빈번한 가비지 수집 결정

  • 타임 라인의 빈번한 상승 및 하락
  • 작업 관리자에서 빈번한 데이터 증가 및 감소

네, 코드 최적화 소개

JavaScript 성능을 정확하게 테스트하는 방법

  • 기본적으로 수학적 통계 및 분석을 위해 많은 수의 실행 샘플을 수집합니다.
  • Benchmark.js를 기반으로 https://jsperf.com을 사용 합니다 (서버가 유지 관리되지 않고 더 이상 사용되지 않습니다. JSBench : https://jsbench.me/ )를 사용 하여 완료 할 수 있습니다.

Jsperf 사용 프로세스

  • GitHub 계정으로 로그인
  • 개인 정보 입력 (필수 아님)
  • 자세한 테스트 케이스 정보 (제목, 슬러그) 입력 : 슬러그는 고유해야합니다.
  • 준비 코드를 입력합니다 (일반적으로 DOM 작업에 사용됨).
  • 필요한 설정 및 해체 코드 입력 : 설정은 사전 준비이며 해체는 모든 코드가 실행 된 후 폐기해야하는 작업입니다.
  • 테스트 코드 스 니펫 작성
  • 여기에 사진 설명 삽입

다섯째, 전역 변수를주의해서 사용하십시오.

주의해야하는 이유

  • 전역 변수는 전역 실행 컨텍스트에서 정의되며 모든 범위 체인의 맨 위에 있습니다.
  • 전역 실행 컨텍스트는 항상 컨텍스트 실행 스택에 존재하며 프로그램이 종료 될 때까지 사라지지 않습니다. 이는 GC가 전역 변수를 발견하는 한 전역 변수를 회수하지 않기 때문에 GC의 작업 상태에 좋지 않습니다. 변수는 여전히 살아 있습니다.
  • 같은 이름의 변수가 로컬 범위에 나타나면 전역을 가리거나 오염시킵니다.

코드 데모 :

var i,str = ''
for(i = 0;i<1000;i++){
    str += i
}

for(i = 0;i<1000;i++){
    let str = '' //函数内部定义的局部变量
    str += i
}

 https://jsperf.com 데모 :

 

 전역 변수로 정의 된 i 코드의 성능이 지역 변수에 정의 된 것만 큼 좋지 않음을 알 수 있습니다.

전역 변수 캐시 :

피할 수없는 전역 변수를 로컬에서 캐시합니다. DOM 요소를 찾으려면이 경우 문서를 사용해야합니다.이 문서는 나중에 정의되지 않지만 현재 전역에 존재합니다. 객체는 직접 내장되어 있으며 이 경우 캐싱 효과를 얻기 위해 로컬 작업에서 재사용해야하는 많은 수의 전역 변수를 배치하도록 선택할 수 있습니다.

코드 데모 :

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>缓存全局变量</title> 
</head> 
<body>
    <input type="button" value="btn" id="btn1">
    <input type="button" value="btn" id="btn2">
    <input type="button" value="btn" id="btn3">
    <input type="button" value="btn" id="btn4">
    <p>1111</p>
    <input type="button" value="btn" id="btn5">
    <input type="button" value="btn" id="btn6">
    <p>222</p>
    <input type="button" value="btn" id="btn7">
    <input type="button" value="btn" id="btn8">
    <p>333</p>
    <input type="button" value="btn" id="btn9">
    <input type="button" value="btn" id="btn10">
<script type="text/javascript">
    function getBtn(){
        let oBtn1 = document.getElementById('btn1')
        let oBtn3 = document.getElementById('btn3')
        let oBtn5 = document.getElementById('btn5')
        let oBtn7 = document.getElementById('btn7')
        let oBtn9 = document.getElementById('btn9')
    }

    function getBtn2(){
        let obj = document
        let oBtn1 = obj.getElementById('btn1')
        let oBtn3 = obj.getElementById('btn3')
        let oBtn5 = obj.getElementById('btn5')
        let oBtn7 = obj.getElementById('btn7')
        let oBtn9 = obj.getElementById('btn9')
    }
</script> 
</body> 
</html>

본문 이외의 내용없이 본문의 html 코드를 준비 단계에 넣습니다.

전역 변수를 캐시하는 코드의 성능이 더 좋습니다.

프로토 타입 객체를 통해 추가 메서드 추가 : 프로토 타입 객체에 인스턴스 객체에 필요한 메서드를 추가합니다. 코드는 다음과 같이 설명됩니다.

var fn1 = function(){
    this.foo = function(){//相当于在某个构造函数内部直接给所有的对象实例添加上了成员方法,后续再使用的过程中都可以拿到
        console.log(11111)
    }
}
let f1 = new fn1()

var fn2 = function(){}
fn2.prototype.foo = function(){//通过原型对象添加方法
    console.log(11111)
}

let f2 = new fn2()

 

 프로토 타입 객체를 통해 메소드를 추가하는 것이 좋습니다.

 

추천

출처blog.csdn.net/weixin_41962912/article/details/110135353